Commit 0df63e44c3e315ec0fe427ae62558231864108bd
Committed by
Jaroslav Kysela
1 parent
f001c3acf6
Exists in
master
and in
7 other branches
[ALSA] Add O_APPEND flag support to PCM
Added O_APPEND flag support to PCM to enable shared substreams among multiple processes. This mechanism is used by dmix and dsnoop plugins. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Showing 8 changed files with 99 additions and 45 deletions Side-by-side Diff
include/sound/asound.h
... | ... | @@ -137,7 +137,7 @@ |
137 | 137 | * * |
138 | 138 | *****************************************************************************/ |
139 | 139 | |
140 | -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 7) | |
140 | +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 8) | |
141 | 141 | |
142 | 142 | typedef unsigned long snd_pcm_uframes_t; |
143 | 143 | typedef signed long snd_pcm_sframes_t; |
include/sound/pcm.h
... | ... | @@ -368,7 +368,8 @@ |
368 | 368 | struct snd_pcm_group *group; /* pointer to current group */ |
369 | 369 | /* -- assigned files -- */ |
370 | 370 | void *file; |
371 | - struct file *ffile; | |
371 | + int ref_count; | |
372 | + unsigned int f_flags; | |
372 | 373 | void (*pcm_release)(struct snd_pcm_substream *); |
373 | 374 | #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) |
374 | 375 | /* -- OSS things -- */ |
... | ... | @@ -387,7 +388,7 @@ |
387 | 388 | unsigned int hw_opened: 1; |
388 | 389 | }; |
389 | 390 | |
390 | -#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL) | |
391 | +#define SUBSTREAM_BUSY(substream) ((substream)->ref_count > 0) | |
391 | 392 | |
392 | 393 | |
393 | 394 | struct snd_pcm_str { |
sound/core/oss/pcm_oss.c
... | ... | @@ -1331,7 +1331,7 @@ |
1331 | 1331 | if (runtime->oss.period_ptr == 0 || |
1332 | 1332 | runtime->oss.period_ptr == runtime->oss.buffer_used) |
1333 | 1333 | runtime->oss.buffer_used = 0; |
1334 | - else if ((substream->ffile->f_flags & O_NONBLOCK) != 0) | |
1334 | + else if ((substream->f_flags & O_NONBLOCK) != 0) | |
1335 | 1335 | return xfer > 0 ? xfer : -EAGAIN; |
1336 | 1336 | } |
1337 | 1337 | } else { |
... | ... | @@ -1344,7 +1344,7 @@ |
1344 | 1344 | buf += tmp; |
1345 | 1345 | bytes -= tmp; |
1346 | 1346 | xfer += tmp; |
1347 | - if ((substream->ffile->f_flags & O_NONBLOCK) != 0 && | |
1347 | + if ((substream->f_flags & O_NONBLOCK) != 0 && | |
1348 | 1348 | tmp != runtime->oss.period_bytes) |
1349 | 1349 | break; |
1350 | 1350 | } |
1351 | 1351 | |
... | ... | @@ -1582,10 +1582,10 @@ |
1582 | 1582 | * finish sync: drain the buffer |
1583 | 1583 | */ |
1584 | 1584 | __direct: |
1585 | - saved_f_flags = substream->ffile->f_flags; | |
1586 | - substream->ffile->f_flags &= ~O_NONBLOCK; | |
1585 | + saved_f_flags = substream->f_flags; | |
1586 | + substream->f_flags &= ~O_NONBLOCK; | |
1587 | 1587 | err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); |
1588 | - substream->ffile->f_flags = saved_f_flags; | |
1588 | + substream->f_flags = saved_f_flags; | |
1589 | 1589 | if (err < 0) |
1590 | 1590 | return err; |
1591 | 1591 | runtime->oss.prepare = 1; |
1592 | 1592 | |
... | ... | @@ -2164,9 +2164,9 @@ |
2164 | 2164 | substream->oss.oss = 1; |
2165 | 2165 | substream->oss.setup = *setup; |
2166 | 2166 | if (setup->nonblock) |
2167 | - substream->ffile->f_flags |= O_NONBLOCK; | |
2167 | + substream->f_flags |= O_NONBLOCK; | |
2168 | 2168 | else if (setup->block) |
2169 | - substream->ffile->f_flags &= ~O_NONBLOCK; | |
2169 | + substream->f_flags &= ~O_NONBLOCK; | |
2170 | 2170 | runtime = substream->runtime; |
2171 | 2171 | runtime->oss.params = 1; |
2172 | 2172 | runtime->oss.trigger = 1; |
... | ... | @@ -2223,6 +2223,7 @@ |
2223 | 2223 | (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX)) |
2224 | 2224 | f_mode = FMODE_WRITE; |
2225 | 2225 | |
2226 | + file->f_flags &= ~O_APPEND; | |
2226 | 2227 | for (idx = 0; idx < 2; idx++) { |
2227 | 2228 | if (setup[idx].disable) |
2228 | 2229 | continue; |
... | ... | @@ -2540,6 +2541,7 @@ |
2540 | 2541 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; |
2541 | 2542 | if (substream == NULL) |
2542 | 2543 | return -ENXIO; |
2544 | + substream->f_flags = file->f_flags & O_NONBLOCK; | |
2543 | 2545 | #ifndef OSS_DEBUG |
2544 | 2546 | return snd_pcm_oss_read1(substream, buf, count); |
2545 | 2547 | #else |
... | ... | @@ -2561,6 +2563,7 @@ |
2561 | 2563 | substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; |
2562 | 2564 | if (substream == NULL) |
2563 | 2565 | return -ENXIO; |
2566 | + substream->f_flags = file->f_flags & O_NONBLOCK; | |
2564 | 2567 | result = snd_pcm_oss_write1(substream, buf, count); |
2565 | 2568 | #ifdef OSS_DEBUG |
2566 | 2569 | printk("pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result); |
sound/core/pcm.c
... | ... | @@ -829,6 +829,26 @@ |
829 | 829 | return -EINVAL; |
830 | 830 | } |
831 | 831 | |
832 | + if (file->f_flags & O_APPEND) { | |
833 | + if (prefer_subdevice < 0) { | |
834 | + if (pstr->substream_count > 1) | |
835 | + return -EINVAL; /* must be unique */ | |
836 | + substream = pstr->substream; | |
837 | + } else { | |
838 | + for (substream = pstr->substream; substream; | |
839 | + substream = substream->next) | |
840 | + if (substream->number == prefer_subdevice) | |
841 | + break; | |
842 | + } | |
843 | + if (! substream) | |
844 | + return -ENODEV; | |
845 | + if (! SUBSTREAM_BUSY(substream)) | |
846 | + return -EBADFD; | |
847 | + substream->ref_count++; | |
848 | + *rsubstream = substream; | |
849 | + return 0; | |
850 | + } | |
851 | + | |
832 | 852 | if (prefer_subdevice >= 0) { |
833 | 853 | for (substream = pstr->substream; substream; substream = substream->next) |
834 | 854 | if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) |
... | ... | @@ -873,7 +893,8 @@ |
873 | 893 | |
874 | 894 | substream->runtime = runtime; |
875 | 895 | substream->private_data = pcm->private_data; |
876 | - substream->ffile = file; | |
896 | + substream->ref_count = 1; | |
897 | + substream->f_flags = file->f_flags; | |
877 | 898 | pstr->substream_opened++; |
878 | 899 | *rsubstream = substream; |
879 | 900 | return 0; |
... | ... | @@ -882,7 +903,7 @@ |
882 | 903 | void snd_pcm_detach_substream(struct snd_pcm_substream *substream) |
883 | 904 | { |
884 | 905 | struct snd_pcm_runtime *runtime; |
885 | - substream->file = NULL; | |
906 | + | |
886 | 907 | runtime = substream->runtime; |
887 | 908 | snd_assert(runtime != NULL, return); |
888 | 909 | if (runtime->private_free != NULL) |
sound/core/pcm_compat.c
... | ... | @@ -497,9 +497,9 @@ |
497 | 497 | case SNDRV_PCM_IOCTL_LINK: |
498 | 498 | case SNDRV_PCM_IOCTL_UNLINK: |
499 | 499 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
500 | - return snd_pcm_playback_ioctl1(substream, cmd, argp); | |
500 | + return snd_pcm_playback_ioctl1(file, substream, cmd, argp); | |
501 | 501 | else |
502 | - return snd_pcm_capture_ioctl1(substream, cmd, argp); | |
502 | + return snd_pcm_capture_ioctl1(file, substream, cmd, argp); | |
503 | 503 | case SNDRV_PCM_IOCTL_HW_REFINE32: |
504 | 504 | return snd_pcm_ioctl_hw_params_compat(substream, 1, argp); |
505 | 505 | case SNDRV_PCM_IOCTL_HW_PARAMS32: |
sound/core/pcm_lib.c
... | ... | @@ -1782,7 +1782,7 @@ |
1782 | 1782 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) |
1783 | 1783 | return -EBADFD; |
1784 | 1784 | |
1785 | - nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); | |
1785 | + nonblock = !!(substream->f_flags & O_NONBLOCK); | |
1786 | 1786 | |
1787 | 1787 | if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && |
1788 | 1788 | runtime->channels > 1) |
... | ... | @@ -1847,7 +1847,7 @@ |
1847 | 1847 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) |
1848 | 1848 | return -EBADFD; |
1849 | 1849 | |
1850 | - nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); | |
1850 | + nonblock = !!(substream->f_flags & O_NONBLOCK); | |
1851 | 1851 | |
1852 | 1852 | if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) |
1853 | 1853 | return -EINVAL; |
... | ... | @@ -2059,7 +2059,7 @@ |
2059 | 2059 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) |
2060 | 2060 | return -EBADFD; |
2061 | 2061 | |
2062 | - nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); | |
2062 | + nonblock = !!(substream->f_flags & O_NONBLOCK); | |
2063 | 2063 | if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) |
2064 | 2064 | return -EINVAL; |
2065 | 2065 | return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); |
... | ... | @@ -2118,7 +2118,7 @@ |
2118 | 2118 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) |
2119 | 2119 | return -EBADFD; |
2120 | 2120 | |
2121 | - nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); | |
2121 | + nonblock = !!(substream->f_flags & O_NONBLOCK); | |
2122 | 2122 | if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) |
2123 | 2123 | return -EINVAL; |
2124 | 2124 | return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); |
sound/core/pcm_native.c
... | ... | @@ -1284,13 +1284,16 @@ |
1284 | 1284 | /* |
1285 | 1285 | * prepare ioctl |
1286 | 1286 | */ |
1287 | -static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream, int state) | |
1287 | +/* we use the second argument for updating f_flags */ | |
1288 | +static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream, | |
1289 | + int f_flags) | |
1288 | 1290 | { |
1289 | 1291 | struct snd_pcm_runtime *runtime = substream->runtime; |
1290 | 1292 | if (runtime->status->state == SNDRV_PCM_STATE_OPEN) |
1291 | 1293 | return -EBADFD; |
1292 | 1294 | if (snd_pcm_running(substream)) |
1293 | 1295 | return -EBUSY; |
1296 | + substream->f_flags = f_flags; | |
1294 | 1297 | return 0; |
1295 | 1298 | } |
1296 | 1299 | |
1297 | 1300 | |
1298 | 1301 | |
1299 | 1302 | |
1300 | 1303 | |
... | ... | @@ -1319,17 +1322,26 @@ |
1319 | 1322 | /** |
1320 | 1323 | * snd_pcm_prepare |
1321 | 1324 | * @substream: the PCM substream instance |
1325 | + * @file: file to refer f_flags | |
1322 | 1326 | * |
1323 | 1327 | * Prepare the PCM substream to be triggerable. |
1324 | 1328 | */ |
1325 | -static int snd_pcm_prepare(struct snd_pcm_substream *substream) | |
1329 | +static int snd_pcm_prepare(struct snd_pcm_substream *substream, | |
1330 | + struct file *file) | |
1326 | 1331 | { |
1327 | 1332 | int res; |
1328 | 1333 | struct snd_card *card = substream->pcm->card; |
1334 | + int f_flags; | |
1329 | 1335 | |
1336 | + if (file) | |
1337 | + f_flags = file->f_flags; | |
1338 | + else | |
1339 | + f_flags = substream->f_flags; | |
1340 | + | |
1330 | 1341 | snd_power_lock(card); |
1331 | 1342 | if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) |
1332 | - res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, 0); | |
1343 | + res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, | |
1344 | + substream, f_flags); | |
1333 | 1345 | snd_power_unlock(card); |
1334 | 1346 | return res; |
1335 | 1347 | } |
... | ... | @@ -1340,7 +1352,7 @@ |
1340 | 1352 | |
1341 | 1353 | static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state) |
1342 | 1354 | { |
1343 | - if (substream->ffile->f_flags & O_NONBLOCK) | |
1355 | + if (substream->f_flags & O_NONBLOCK) | |
1344 | 1356 | return -EAGAIN; |
1345 | 1357 | substream->runtime->trigger_master = substream; |
1346 | 1358 | return 0; |
... | ... | @@ -2015,6 +2027,10 @@ |
2015 | 2027 | |
2016 | 2028 | void snd_pcm_release_substream(struct snd_pcm_substream *substream) |
2017 | 2029 | { |
2030 | + substream->ref_count--; | |
2031 | + if (substream->ref_count > 0) | |
2032 | + return; | |
2033 | + | |
2018 | 2034 | snd_pcm_drop(substream); |
2019 | 2035 | if (substream->hw_opened) { |
2020 | 2036 | if (substream->ops->hw_free != NULL) |
... | ... | @@ -2041,6 +2057,11 @@ |
2041 | 2057 | err = snd_pcm_attach_substream(pcm, stream, file, &substream); |
2042 | 2058 | if (err < 0) |
2043 | 2059 | return err; |
2060 | + if (substream->ref_count > 1) { | |
2061 | + *rsubstream = substream; | |
2062 | + return 0; | |
2063 | + } | |
2064 | + | |
2044 | 2065 | substream->no_mmap_ctrl = 0; |
2045 | 2066 | err = snd_pcm_hw_constraints_init(substream); |
2046 | 2067 | if (err < 0) { |
2047 | 2068 | |
... | ... | @@ -2086,17 +2107,20 @@ |
2086 | 2107 | if (err < 0) |
2087 | 2108 | return err; |
2088 | 2109 | |
2089 | - pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); | |
2090 | - if (pcm_file == NULL) { | |
2091 | - snd_pcm_release_substream(substream); | |
2092 | - return -ENOMEM; | |
2110 | + if (substream->ref_count > 1) | |
2111 | + pcm_file = substream->file; | |
2112 | + else { | |
2113 | + pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); | |
2114 | + if (pcm_file == NULL) { | |
2115 | + snd_pcm_release_substream(substream); | |
2116 | + return -ENOMEM; | |
2117 | + } | |
2118 | + str = substream->pstr; | |
2119 | + substream->file = pcm_file; | |
2120 | + substream->pcm_release = pcm_release_private; | |
2121 | + pcm_file->substream = substream; | |
2122 | + snd_pcm_add_file(str, pcm_file); | |
2093 | 2123 | } |
2094 | - str = substream->pstr; | |
2095 | - substream->file = pcm_file; | |
2096 | - substream->pcm_release = pcm_release_private; | |
2097 | - pcm_file->substream = substream; | |
2098 | - snd_pcm_add_file(str, pcm_file); | |
2099 | - | |
2100 | 2124 | file->private_data = pcm_file; |
2101 | 2125 | *rpcm_file = pcm_file; |
2102 | 2126 | return 0; |
... | ... | @@ -2506,7 +2530,8 @@ |
2506 | 2530 | return 0; |
2507 | 2531 | } |
2508 | 2532 | |
2509 | -static int snd_pcm_common_ioctl1(struct snd_pcm_substream *substream, | |
2533 | +static int snd_pcm_common_ioctl1(struct file *file, | |
2534 | + struct snd_pcm_substream *substream, | |
2510 | 2535 | unsigned int cmd, void __user *arg) |
2511 | 2536 | { |
2512 | 2537 | snd_assert(substream != NULL, return -ENXIO); |
... | ... | @@ -2531,7 +2556,7 @@ |
2531 | 2556 | case SNDRV_PCM_IOCTL_CHANNEL_INFO: |
2532 | 2557 | return snd_pcm_channel_info_user(substream, arg); |
2533 | 2558 | case SNDRV_PCM_IOCTL_PREPARE: |
2534 | - return snd_pcm_prepare(substream); | |
2559 | + return snd_pcm_prepare(substream, file); | |
2535 | 2560 | case SNDRV_PCM_IOCTL_RESET: |
2536 | 2561 | return snd_pcm_reset(substream); |
2537 | 2562 | case SNDRV_PCM_IOCTL_START: |
... | ... | @@ -2573,7 +2598,8 @@ |
2573 | 2598 | return -ENOTTY; |
2574 | 2599 | } |
2575 | 2600 | |
2576 | -static int snd_pcm_playback_ioctl1(struct snd_pcm_substream *substream, | |
2601 | +static int snd_pcm_playback_ioctl1(struct file *file, | |
2602 | + struct snd_pcm_substream *substream, | |
2577 | 2603 | unsigned int cmd, void __user *arg) |
2578 | 2604 | { |
2579 | 2605 | snd_assert(substream != NULL, return -ENXIO); |
2580 | 2606 | |
... | ... | @@ -2649,10 +2675,11 @@ |
2649 | 2675 | return result < 0 ? result : 0; |
2650 | 2676 | } |
2651 | 2677 | } |
2652 | - return snd_pcm_common_ioctl1(substream, cmd, arg); | |
2678 | + return snd_pcm_common_ioctl1(file, substream, cmd, arg); | |
2653 | 2679 | } |
2654 | 2680 | |
2655 | -static int snd_pcm_capture_ioctl1(struct snd_pcm_substream *substream, | |
2681 | +static int snd_pcm_capture_ioctl1(struct file *file, | |
2682 | + struct snd_pcm_substream *substream, | |
2656 | 2683 | unsigned int cmd, void __user *arg) |
2657 | 2684 | { |
2658 | 2685 | snd_assert(substream != NULL, return -ENXIO); |
... | ... | @@ -2728,7 +2755,7 @@ |
2728 | 2755 | return result < 0 ? result : 0; |
2729 | 2756 | } |
2730 | 2757 | } |
2731 | - return snd_pcm_common_ioctl1(substream, cmd, arg); | |
2758 | + return snd_pcm_common_ioctl1(file, substream, cmd, arg); | |
2732 | 2759 | } |
2733 | 2760 | |
2734 | 2761 | static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd, |
... | ... | @@ -2741,7 +2768,8 @@ |
2741 | 2768 | if (((cmd >> 8) & 0xff) != 'A') |
2742 | 2769 | return -ENOTTY; |
2743 | 2770 | |
2744 | - return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void __user *)arg); | |
2771 | + return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd, | |
2772 | + (void __user *)arg); | |
2745 | 2773 | } |
2746 | 2774 | |
2747 | 2775 | static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd, |
... | ... | @@ -2754,7 +2782,8 @@ |
2754 | 2782 | if (((cmd >> 8) & 0xff) != 'A') |
2755 | 2783 | return -ENOTTY; |
2756 | 2784 | |
2757 | - return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void __user *)arg); | |
2785 | + return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd, | |
2786 | + (void __user *)arg); | |
2758 | 2787 | } |
2759 | 2788 | |
2760 | 2789 | int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, |
2761 | 2790 | |
... | ... | @@ -2766,12 +2795,12 @@ |
2766 | 2795 | fs = snd_enter_user(); |
2767 | 2796 | switch (substream->stream) { |
2768 | 2797 | case SNDRV_PCM_STREAM_PLAYBACK: |
2769 | - result = snd_pcm_playback_ioctl1(substream, | |
2770 | - cmd, (void __user *)arg); | |
2798 | + result = snd_pcm_playback_ioctl1(NULL, substream, cmd, | |
2799 | + (void __user *)arg); | |
2771 | 2800 | break; |
2772 | 2801 | case SNDRV_PCM_STREAM_CAPTURE: |
2773 | - result = snd_pcm_capture_ioctl1(substream, | |
2774 | - cmd, (void __user *)arg); | |
2802 | + result = snd_pcm_capture_ioctl1(NULL, substream, cmd, | |
2803 | + (void __user *)arg); | |
2775 | 2804 | break; |
2776 | 2805 | default: |
2777 | 2806 | result = -EINVAL; |
sound/usb/usx2y/usx2yhwdeppcm.c