diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-06 10:56:51 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-06 10:56:51 -0700 |
commit | 920f2ecdf6c3b3526f60fbd38c68597953cad3ee (patch) | |
tree | 18188922ba38a5c53ee8d17032eb5c46dffc7fa2 /sound/core | |
parent | 9ced560b82606b35adb33a27012a148d418a4c1f (diff) | |
parent | fc18282cdcba984ab89c74d7e844c10114ae0795 (diff) | |
download | linux-920f2ecdf6c3b3526f60fbd38c68597953cad3ee.tar.gz linux-920f2ecdf6c3b3526f60fbd38c68597953cad3ee.tar.bz2 linux-920f2ecdf6c3b3526f60fbd38c68597953cad3ee.zip |
Merge tag 'sound-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"This development cycle resulted in a fair amount of changes in both
core and driver sides. The most significant change in ALSA core is
about PCM. Also the support of of-graph card and the new DAPM widget
for DSP are noteworthy changes in ASoC core. And there're lots of
small changes splat over the tree, as you can see in diffstat.
Below are a few highlights:
ALSA core:
- Removal of set_fs() hackery from PCM core stuff, and the code
reorganization / optimization thereafter
- Improved support of PCM ack ops, and a new ABI for improved
control/status mmap handling
- Lots of constifications in various codes
ASoC core:
- The support of of-graph card, which may work as a better generic
device for a replacement of simple-card
- New widget types intended mainly for use with DSPs
ASoC drivers:
- New drivers for Allwinner V3s SoCs
- Ensonic ES8316 codec support
- More Intel SKL and KBL works
- More device support for Intel SST Atom (mostly for cheap tablets
and 2-in-1 devices)
- Support for Rockchip PDM controllers
- Support for STM32 I2S and S/PDIF controllers
- Support for ZTE AUD96P22 codecs
HD-audio:
- Support of new Realtek codecs (ALC215/ALC285/ALC289), more quirks
for HP and Dell machines
- A few more fixes for i915 component binding"
* tag 'sound-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (418 commits)
ALSA: hda - Fix unbalance of i915 module refcount
ASoC: Intel: Skylake: Remove driver debugfs exit
ASoC: Intel: Skylake: explicitly add the headers sst-dsp.h
ALSA: hda/realtek - Remove GPIO_MASK
ALSA: hda/realtek - Fix typo of pincfg for Dell quirk
ALSA: pcm: add a documentation for tracepoints
ALSA: atmel: ac97c: fix error return code in atmel_ac97c_probe()
ALSA: x86: fix error return code in hdmi_lpe_audio_probe()
ASoC: Intel: Skylake: Add support to read firmware registers
ASoC: Intel: Skylake: Add sram address to sst_addr structure
ASoC: Intel: Skylake: Debugfs facility to dump module config
ASoC: Intel: Skylake: Add debugfs support
ASoC: fix semicolon.cocci warnings
ASoC: rt5645: Add quirk override by module option
ASoC: rsnd: make arrays path and cmd_case static const
ASoC: audio-graph-card: add widgets and routing for external amplifier support
ASoC: audio-graph-card: update bindings for amplifier support
ASoC: rt5665: calibration should be done before jack detection
ASoC: rsnd: constify dev_pm_ops structures.
ASoC: nau8825: change crosstalk-bypass property to bool type
...
Diffstat (limited to 'sound/core')
42 files changed, 1311 insertions, 1272 deletions
diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 9749f9e8b45c..6e937a8146a1 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -18,8 +18,12 @@ config SND_DMAENGINE_PCM config SND_HWDEP tristate +config SND_SEQ_DEVICE + tristate + config SND_RAWMIDI tristate + select SND_SEQ_DEVICE if SND_SEQUENCER != n config SND_COMPRESS_OFFLOAD tristate @@ -33,38 +37,15 @@ config SND_JACK_INPUT_DEV depends on SND_JACK default y if INPUT=y || INPUT=SND -config SND_SEQUENCER - tristate "Sequencer support" - select SND_TIMER - help - Say Y or M to enable MIDI sequencer and router support. This - feature allows routing and enqueueing of MIDI events. Events - can be processed at a given time. - - Many programs require this feature, so you should enable it - unless you know what you're doing. - -config SND_SEQ_DUMMY - tristate "Sequencer dummy client" - depends on SND_SEQUENCER - help - Say Y here to enable the dummy sequencer client. This client - is a simple MIDI-through client: all normal input events are - redirected to the output port immediately. - - You don't need this unless you want to connect many MIDI - devices or applications together. - - To compile this driver as a module, choose M here: the module - will be called snd-seq-dummy. - config SND_OSSEMUL + bool "Enable OSS Emulation" select SOUND_OSS_CORE - bool + help + This option enables the build of OSS emulation layer. config SND_MIXER_OSS tristate "OSS Mixer API" - select SND_OSSEMUL + depends on SND_OSSEMUL help To enable OSS mixer API emulation (/dev/mixer*), say Y here and read <file:Documentation/sound/alsa/OSS-Emulation.txt>. @@ -76,7 +57,7 @@ config SND_MIXER_OSS config SND_PCM_OSS tristate "OSS PCM (digital audio) API" - select SND_OSSEMUL + depends on SND_OSSEMUL select SND_PCM help To enable OSS digital audio (PCM) emulation (/dev/dsp*), say Y @@ -107,20 +88,6 @@ config SND_PCM_TIMER For some embedded devices, we may disable it to reduce memory footprint, about 20KB on x86_64 platform. -config SND_SEQUENCER_OSS - bool "OSS Sequencer API" - depends on SND_SEQUENCER - select SND_OSSEMUL - help - Say Y here to enable OSS sequencer emulation (both - /dev/sequencer and /dev/music interfaces). - - Many programs still use the OSS API, so say Y. - - If you choose M in "Sequencer support" (SND_SEQUENCER), - this will be compiled as a module. The module will be called - snd-seq-oss. - config SND_HRTIMER tristate "HR-timer backend support" depends on HIGH_RES_TIMERS @@ -133,14 +100,6 @@ config SND_HRTIMER To compile this driver as a module, choose M here: the module will be called snd-hrtimer. -config SND_SEQ_HRTIMER_DEFAULT - bool "Use HR-timer as default sequencer timer" - depends on SND_HRTIMER && SND_SEQUENCER - default y - help - Say Y here to use the HR-timer backend as the default sequencer - timer. - config SND_DYNAMIC_MINORS bool "Dynamic device file minor numbers" help diff --git a/sound/core/Makefile b/sound/core/Makefile index e85d9dd12c2d..e2066e2ef9f8 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -22,6 +22,7 @@ snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o # for trace-points CFLAGS_pcm_lib.o := -I$(src) +CFLAGS_pcm_native.o := -I$(src) snd-pcm-dmaengine-objs := pcm_dmaengine.o @@ -30,6 +31,7 @@ snd-timer-objs := timer.o snd-hrtimer-objs := hrtimer.o snd-rtctimer-objs := rtctimer.o snd-hwdep-objs := hwdep.o +snd-seq-device-objs := seq_device.o snd-compress-objs := compress_offload.o @@ -39,6 +41,7 @@ obj-$(CONFIG_SND_TIMER) += snd-timer.o obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o obj-$(CONFIG_SND_PCM) += snd-pcm.o obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o +obj-$(CONFIG_SND_SEQ_DEVICE) += snd-seq-device.o obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o obj-$(CONFIG_SND_OSSEMUL) += oss/ diff --git a/sound/core/control.c b/sound/core/control.c index 6362da17ac3f..3c6be1452e35 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -747,65 +747,45 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, static int snd_ctl_elem_list(struct snd_card *card, struct snd_ctl_elem_list __user *_list) { - struct list_head *plist; struct snd_ctl_elem_list list; struct snd_kcontrol *kctl; - struct snd_ctl_elem_id *dst, *id; + struct snd_ctl_elem_id id; unsigned int offset, space, jidx; + int err = 0; if (copy_from_user(&list, _list, sizeof(list))) return -EFAULT; offset = list.offset; space = list.space; - /* try limit maximum space */ - if (space > 16384) - return -ENOMEM; + + down_read(&card->controls_rwsem); + list.count = card->controls_count; + list.used = 0; if (space > 0) { - /* allocate temporary buffer for atomic operation */ - dst = vmalloc(space * sizeof(struct snd_ctl_elem_id)); - if (dst == NULL) - return -ENOMEM; - down_read(&card->controls_rwsem); - list.count = card->controls_count; - plist = card->controls.next; - while (plist != &card->controls) { - if (offset == 0) - break; - kctl = snd_kcontrol(plist); - if (offset < kctl->count) - break; - offset -= kctl->count; - plist = plist->next; - } - list.used = 0; - id = dst; - while (space > 0 && plist != &card->controls) { - kctl = snd_kcontrol(plist); - for (jidx = offset; space > 0 && jidx < kctl->count; jidx++) { - snd_ctl_build_ioff(id, kctl, jidx); - id++; - space--; + list_for_each_entry(kctl, &card->controls, list) { + if (offset >= kctl->count) { + offset -= kctl->count; + continue; + } + for (jidx = offset; jidx < kctl->count; jidx++) { + snd_ctl_build_ioff(&id, kctl, jidx); + if (copy_to_user(list.pids + list.used, &id, + sizeof(id))) { + err = -EFAULT; + goto out; + } list.used++; + if (!--space) + goto out; } - plist = plist->next; offset = 0; } - up_read(&card->controls_rwsem); - if (list.used > 0 && - copy_to_user(list.pids, dst, - list.used * sizeof(struct snd_ctl_elem_id))) { - vfree(dst); - return -EFAULT; - } - vfree(dst); - } else { - down_read(&card->controls_rwsem); - list.count = card->controls_count; - up_read(&card->controls_rwsem); } - if (copy_to_user(_list, &list, sizeof(list))) - return -EFAULT; - return 0; + out: + up_read(&card->controls_rwsem); + if (!err && copy_to_user(_list, &list, sizeof(list))) + err = -EFAULT; + return err; } static bool validate_element_member_dimension(struct snd_ctl_elem_info *info) diff --git a/sound/core/ctljack.c b/sound/core/ctljack.c index 84a3cd683068..0249d5e6ac23 100644 --- a/sound/core/ctljack.c +++ b/sound/core/ctljack.c @@ -23,7 +23,7 @@ static int jack_detect_kctl_get(struct snd_kcontrol *kcontrol, return 0; } -static struct snd_kcontrol_new jack_detect_kctl = { +static const struct snd_kcontrol_new jack_detect_kctl = { /* name is filled later */ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .access = SNDRV_CTL_ELEM_ACCESS_READ, diff --git a/sound/core/info.c b/sound/core/info.c index 8ab72e0f5932..bcf6a48cc70d 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -344,12 +344,12 @@ static ssize_t snd_info_text_entry_write(struct file *file, } } if (next > buf->len) { - char *nbuf = krealloc(buf->buffer, PAGE_ALIGN(next), - GFP_KERNEL | __GFP_ZERO); + char *nbuf = kvzalloc(PAGE_ALIGN(next), GFP_KERNEL); if (!nbuf) { err = -ENOMEM; goto error; } + kvfree(buf->buffer); buf->buffer = nbuf; buf->len = PAGE_ALIGN(next); } @@ -427,7 +427,7 @@ static int snd_info_text_entry_release(struct inode *inode, struct file *file) single_release(inode, file); kfree(data->rbuffer); if (data->wbuffer) { - kfree(data->wbuffer->buffer); + kvfree(data->wbuffer->buffer); kfree(data->wbuffer); } @@ -652,7 +652,6 @@ int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len) *line = '\0'; return 0; } - EXPORT_SYMBOL(snd_info_get_line); /** @@ -690,7 +689,6 @@ const char *snd_info_get_str(char *dest, const char *src, int len) src++; return src; } - EXPORT_SYMBOL(snd_info_get_str); /* @@ -748,7 +746,6 @@ struct snd_info_entry *snd_info_create_module_entry(struct module * module, entry->module = module; return entry; } - EXPORT_SYMBOL(snd_info_create_module_entry); /** @@ -772,7 +769,6 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card, } return entry; } - EXPORT_SYMBOL(snd_info_create_card_entry); static void snd_info_disconnect(struct snd_info_entry *entry) @@ -815,7 +811,6 @@ void snd_info_free_entry(struct snd_info_entry * entry) entry->private_free(entry); kfree(entry); } - EXPORT_SYMBOL(snd_info_free_entry); /** @@ -858,7 +853,6 @@ int snd_info_register(struct snd_info_entry * entry) mutex_unlock(&info_mutex); return 0; } - EXPORT_SYMBOL(snd_info_register); /* diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index 1478c8dfd473..f479374b6bd8 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -61,7 +61,6 @@ int snd_oss_info_register(int dev, int num, char *string) mutex_unlock(&strings); return 0; } - EXPORT_SYMBOL(snd_oss_info_register); static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int dev) diff --git a/sound/core/init.c b/sound/core/init.c index d61d2b3cd521..b4365bcf28a7 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -452,7 +452,6 @@ int snd_card_disconnect(struct snd_card *card) #endif return 0; } - EXPORT_SYMBOL(snd_card_disconnect); static int snd_card_do_free(struct snd_card *card) @@ -718,7 +717,7 @@ int snd_card_add_dev_attr(struct snd_card *card, dev_err(card->dev, "Too many groups assigned\n"); return -ENOSPC; -}; +} EXPORT_SYMBOL_GPL(snd_card_add_dev_attr); /** @@ -775,7 +774,6 @@ int snd_card_register(struct snd_card *card) #endif return 0; } - EXPORT_SYMBOL(snd_card_register); #ifdef CONFIG_SND_PROC_FS @@ -895,7 +893,6 @@ int snd_component_add(struct snd_card *card, const char *component) strcat(card->components, component); return 0; } - EXPORT_SYMBOL(snd_component_add); /** @@ -930,7 +927,6 @@ int snd_card_file_add(struct snd_card *card, struct file *file) spin_unlock(&card->files_lock); return 0; } - EXPORT_SYMBOL(snd_card_file_add); /** @@ -972,7 +968,6 @@ int snd_card_file_remove(struct snd_card *card, struct file *file) put_device(&card->card_dev); return 0; } - EXPORT_SYMBOL(snd_card_file_remove); #ifdef CONFIG_PM @@ -1012,6 +1007,5 @@ int snd_power_wait(struct snd_card *card, unsigned int power_state) remove_wait_queue(&card->power_sleep, &wait); return result; } - EXPORT_SYMBOL(snd_power_wait); #endif /* CONFIG_PM */ diff --git a/sound/core/isadma.c b/sound/core/isadma.c index 31e8544d7f2d..7a8515abb5f9 100644 --- a/sound/core/isadma.c +++ b/sound/core/isadma.c @@ -55,7 +55,6 @@ void snd_dma_program(unsigned long dma, enable_dma(dma); release_dma_lock(flags); } - EXPORT_SYMBOL(snd_dma_program); /** @@ -73,7 +72,6 @@ void snd_dma_disable(unsigned long dma) disable_dma(dma); release_dma_lock(flags); } - EXPORT_SYMBOL(snd_dma_disable); /** @@ -113,5 +111,4 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size) else return size - result; } - EXPORT_SYMBOL(snd_dma_pointer); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index f05cb6a8cbe0..7f89d3c79a4b 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -54,6 +54,7 @@ void *snd_malloc_pages(size_t size, gfp_t gfp_flags) pg = get_order(size); return (void *) __get_free_pages(gfp_flags, pg); } +EXPORT_SYMBOL(snd_malloc_pages); /** * snd_free_pages - release the pages @@ -71,6 +72,7 @@ void snd_free_pages(void *ptr, size_t size) pg = get_order(size); free_pages((unsigned long) ptr, pg); } +EXPORT_SYMBOL(snd_free_pages); /* * @@ -217,6 +219,7 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->bytes = size; return 0; } +EXPORT_SYMBOL(snd_dma_alloc_pages); /** * snd_dma_alloc_pages_fallback - allocate the buffer area according to the given type with fallback @@ -254,6 +257,7 @@ int snd_dma_alloc_pages_fallback(int type, struct device *device, size_t size, return -ENOMEM; return 0; } +EXPORT_SYMBOL(snd_dma_alloc_pages_fallback); /** @@ -287,13 +291,4 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) pr_err("snd-malloc: invalid device type %d\n", dmab->dev.type); } } - -/* - * exports - */ -EXPORT_SYMBOL(snd_dma_alloc_pages); -EXPORT_SYMBOL(snd_dma_alloc_pages_fallback); EXPORT_SYMBOL(snd_dma_free_pages); - -EXPORT_SYMBOL(snd_malloc_pages); -EXPORT_SYMBOL(snd_free_pages); diff --git a/sound/core/memory.c b/sound/core/memory.c index 4cd664efad77..19c9ea90d9bf 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -55,7 +55,6 @@ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size return 0; #endif } - EXPORT_SYMBOL(copy_to_user_fromio); /** @@ -88,5 +87,4 @@ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size return 0; #endif } - EXPORT_SYMBOL(copy_from_user_toio); diff --git a/sound/core/misc.c b/sound/core/misc.c index 21b228046e88..0f818d593c9e 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -48,7 +48,6 @@ void release_and_free_resource(struct resource *res) kfree(res); } } - EXPORT_SYMBOL(release_and_free_resource); #ifdef CONFIG_SND_VERBOSE_PRINTK diff --git a/sound/core/oss/io.c b/sound/core/oss/io.c index 6faa1d719206..d870b2d93135 100644 --- a/sound/core/oss/io.c +++ b/sound/core/oss/io.c @@ -26,9 +26,9 @@ #include "pcm_plugin.h" #define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1) -#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1) +#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count) #define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1) -#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1) +#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count) /* * Basic io plugin diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 2ff9c12d664a..379bf486ccc7 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -395,6 +395,7 @@ int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned l fmixer.mixer = card->mixer_oss; return snd_mixer_oss_ioctl1(&fmixer, cmd, arg); } +EXPORT_SYMBOL(snd_mixer_oss_ioctl_card); #ifdef CONFIG_COMPAT /* all compatible */ @@ -1425,5 +1426,3 @@ static void __exit alsa_mixer_oss_exit(void) module_init(alsa_mixer_oss_init) module_exit(alsa_mixer_oss_exit) - -EXPORT_SYMBOL(snd_mixer_oss_ioctl_card); diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index cd8b7bef8d06..e49f448ee04f 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -67,18 +67,6 @@ static int snd_pcm_oss_get_rate(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_channels(struct snd_pcm_oss_file *pcm_oss_file); static int snd_pcm_oss_get_format(struct snd_pcm_oss_file *pcm_oss_file); -static inline mm_segment_t snd_enter_user(void) -{ - mm_segment_t fs = get_fs(); - set_fs(get_ds()); - return fs; -} - -static inline void snd_leave_user(mm_segment_t fs) -{ - set_fs(fs); -} - /* * helper functions to process hw_params */ @@ -799,7 +787,7 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream, static int choose_rate(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, unsigned int best_rate) { - struct snd_interval *it; + const struct snd_interval *it; struct snd_pcm_hw_params *save; unsigned int rate, prev; @@ -807,7 +795,7 @@ static int choose_rate(struct snd_pcm_substream *substream, if (save == NULL) return -ENOMEM; *save = *params; - it = hw_param_interval(save, SNDRV_PCM_HW_PARAM_RATE); + it = hw_param_interval_c(save, SNDRV_PCM_HW_PARAM_RATE); /* try multiples of the best rate */ rate = best_rate; @@ -848,7 +836,7 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, int direct; snd_pcm_format_t format, sformat; int n; - struct snd_mask sformat_mask; + const struct snd_mask *sformat_mask; struct snd_mask mask; if (trylock) { @@ -891,18 +879,18 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, format = snd_pcm_oss_format_from(runtime->oss.format); - sformat_mask = *hw_param_mask(sparams, SNDRV_PCM_HW_PARAM_FORMAT); + sformat_mask = hw_param_mask_c(sparams, SNDRV_PCM_HW_PARAM_FORMAT); if (direct) sformat = format; else - sformat = snd_pcm_plug_slave_format(format, &sformat_mask); + sformat = snd_pcm_plug_slave_format(format, sformat_mask); if ((__force int)sformat < 0 || - !snd_mask_test(&sformat_mask, (__force int)sformat)) { + !snd_mask_test(sformat_mask, (__force int)sformat)) { for (sformat = (__force snd_pcm_format_t)0; (__force int)sformat <= (__force int)SNDRV_PCM_FORMAT_LAST; sformat = (__force snd_pcm_format_t)((__force int)sformat + 1)) { - if (snd_mask_test(&sformat_mask, (__force int)sformat) && + if (snd_mask_test(sformat_mask, (__force int)sformat) && snd_pcm_oss_format_to(sformat) >= 0) break; } @@ -1191,14 +1179,8 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, const if (ret < 0) break; } - if (in_kernel) { - mm_segment_t fs; - fs = snd_enter_user(); - ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); - snd_leave_user(fs); - } else { - ret = snd_pcm_lib_write(substream, (void __force __user *)ptr, frames); - } + ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true, + frames, in_kernel); if (ret != -EPIPE && ret != -ESTRPIPE) break; /* test, if we can't store new data, because the stream */ @@ -1234,14 +1216,8 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p ret = snd_pcm_oss_capture_position_fixup(substream, &delay); if (ret < 0) break; - if (in_kernel) { - mm_segment_t fs; - fs = snd_enter_user(); - ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); - snd_leave_user(fs); - } else { - ret = snd_pcm_lib_read(substream, (void __force __user *)ptr, frames); - } + ret = __snd_pcm_lib_xfer(substream, (void *)ptr, true, + frames, in_kernel); if (ret == -EPIPE) { if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { ret = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); @@ -1256,7 +1232,8 @@ snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *p return ret; } -snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) +#ifdef CONFIG_SND_PCM_OSS_PLUGINS +snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; int ret; @@ -1273,14 +1250,7 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void if (ret < 0) break; } - if (in_kernel) { - mm_segment_t fs; - fs = snd_enter_user(); - ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames); - snd_leave_user(fs); - } else { - ret = snd_pcm_lib_writev(substream, (void __user **)bufs, frames); - } + ret = snd_pcm_kernel_writev(substream, bufs, frames); if (ret != -EPIPE && ret != -ESTRPIPE) break; @@ -1292,7 +1262,7 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void return ret; } -snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames, int in_kernel) +snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void **bufs, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; int ret; @@ -1313,19 +1283,13 @@ snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void * if (ret < 0) break; } - if (in_kernel) { - mm_segment_t fs; - fs = snd_enter_user(); - ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames); - snd_leave_user(fs); - } else { - ret = snd_pcm_lib_readv(substream, (void __user **)bufs, frames); - } + ret = snd_pcm_kernel_readv(substream, bufs, frames); if (ret != -EPIPE && ret != -ESTRPIPE) break; } return ret; } +#endif /* CONFIG_SND_PCM_OSS_PLUGINS */ static ssize_t snd_pcm_oss_write2(struct snd_pcm_substream *substream, const char *buf, size_t bytes, int in_kernel) { @@ -1650,27 +1614,10 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) size = runtime->control->appl_ptr % runtime->period_size; if (size > 0) { size = runtime->period_size - size; - if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) { - size = (runtime->frame_bits * size) / 8; - while (size > 0) { - mm_segment_t fs; - size_t size1 = size < runtime->oss.period_bytes ? size : runtime->oss.period_bytes; - size -= size1; - size1 *= 8; - size1 /= runtime->sample_bits; - snd_pcm_format_set_silence(runtime->format, - runtime->oss.buffer, - size1); - size1 /= runtime->channels; /* frames */ - fs = snd_enter_user(); - snd_pcm_lib_write(substream, (void __force __user *)runtime->oss.buffer, size1); - snd_leave_user(fs); - } - } else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) { - void __user *buffers[runtime->channels]; - memset(buffers, 0, runtime->channels * sizeof(void *)); - snd_pcm_lib_writev(substream, buffers, size); - } + if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) + snd_pcm_lib_write(substream, NULL, size); + else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + snd_pcm_lib_writev(substream, NULL, size); } mutex_unlock(&runtime->oss.params_lock); /* @@ -1780,7 +1727,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) int direct; struct snd_pcm_hw_params *params; unsigned int formats = 0; - struct snd_mask format_mask; + const struct snd_mask *format_mask; int fmt; if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) @@ -1802,12 +1749,12 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) return -ENOMEM; _snd_pcm_hw_params_any(params); err = snd_pcm_hw_refine(substream, params); - format_mask = *hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + format_mask = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); kfree(params); if (err < 0) return err; for (fmt = 0; fmt < 32; ++fmt) { - if (snd_mask_test(&format_mask, fmt)) { + if (snd_mask_test(format_mask, fmt)) { int f = snd_pcm_oss_format_to(fmt); if (f >= 0) formats |= f; diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c index 727ac44d39f4..cadc93792868 100644 --- a/sound/core/oss/pcm_plugin.c +++ b/sound/core/oss/pcm_plugin.c @@ -266,7 +266,8 @@ snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *plug, snd_pc return frames; } -static int snd_pcm_plug_formats(struct snd_mask *mask, snd_pcm_format_t format) +static int snd_pcm_plug_formats(const struct snd_mask *mask, + snd_pcm_format_t format) { struct snd_mask formats = *mask; u64 linfmts = (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | @@ -309,7 +310,7 @@ static snd_pcm_format_t preferred_formats[] = { }; snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, - struct snd_mask *format_mask) + const struct snd_mask *format_mask) { int i; diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index a5035c2369a6..c9cd29d86efd 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -126,7 +126,7 @@ int snd_pcm_plug_format_plugins(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *slave_params); snd_pcm_format_t snd_pcm_plug_slave_format(snd_pcm_format_t format, - struct snd_mask *format_mask); + const struct snd_mask *format_mask); int snd_pcm_plugin_append(struct snd_pcm_plugin *plugin); @@ -162,17 +162,15 @@ snd_pcm_sframes_t snd_pcm_oss_write3(struct snd_pcm_substream *substream, snd_pcm_sframes_t snd_pcm_oss_read3(struct snd_pcm_substream *substream, char *ptr, snd_pcm_uframes_t size, int in_kernel); snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, - void **bufs, snd_pcm_uframes_t frames, - int in_kernel); + void **bufs, snd_pcm_uframes_t frames); snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, - void **bufs, snd_pcm_uframes_t frames, - int in_kernel); + void **bufs, snd_pcm_uframes_t frames); #else static inline snd_pcm_sframes_t snd_pcm_plug_client_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t drv_size) { return drv_size; } static inline snd_pcm_sframes_t snd_pcm_plug_slave_size(struct snd_pcm_substream *handle, snd_pcm_uframes_t clt_size) { return clt_size; } -static inline int snd_pcm_plug_slave_format(int format, struct snd_mask *format_mask) { return format; } +static inline int snd_pcm_plug_slave_format(int format, const struct snd_mask *format_mask) { return format; } #endif diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 8e980aa678d0..89c7485519cb 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -31,13 +31,17 @@ #include <sound/control.h> #include <sound/info.h> +#include "pcm_local.h" + MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Abramo Bagnara <abramo@alsa-project.org>"); MODULE_DESCRIPTION("Midlevel PCM code for ALSA."); MODULE_LICENSE("GPL"); static LIST_HEAD(snd_pcm_devices); -static LIST_HEAD(snd_pcm_notify_list); static DEFINE_MUTEX(register_mutex); +#if IS_ENABLED(CONFIG_SND_PCM_OSS) +static LIST_HEAD(snd_pcm_notify_list); +#endif static int snd_pcm_free(struct snd_pcm *pcm); static int snd_pcm_dev_free(struct snd_device *device); @@ -884,16 +888,23 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr) put_device(&pstr->dev); } +#if IS_ENABLED(CONFIG_SND_PCM_OSS) +#define pcm_call_notify(pcm, call) \ + do { \ + struct snd_pcm_notify *_notify; \ + list_for_each_entry(_notify, &snd_pcm_notify_list, list) \ + _notify->call(pcm); \ + } while (0) +#else +#define pcm_call_notify(pcm, call) do {} while (0) +#endif + static int snd_pcm_free(struct snd_pcm *pcm) { - struct snd_pcm_notify *notify; - if (!pcm) return 0; - if (!pcm->internal) { - list_for_each_entry(notify, &snd_pcm_notify_list, list) - notify->n_unregister(pcm); - } + if (!pcm->internal) + pcm_call_notify(pcm, n_unregister); if (pcm->private_free) pcm->private_free(pcm); snd_pcm_lib_preallocate_free_for_all(pcm); @@ -1056,7 +1067,7 @@ static struct attribute *pcm_dev_attrs[] = { NULL }; -static struct attribute_group pcm_dev_attr_group = { +static const struct attribute_group pcm_dev_attr_group = { .attrs = pcm_dev_attrs, }; @@ -1069,7 +1080,6 @@ static int snd_pcm_dev_register(struct snd_device *device) { int cidx, err; struct snd_pcm_substream *substream; - struct snd_pcm_notify *notify; struct snd_pcm *pcm; if (snd_BUG_ON(!device || !device->device_data)) @@ -1107,8 +1117,7 @@ static int snd_pcm_dev_register(struct snd_device *device) snd_pcm_timer_init(substream); } - list_for_each_entry(notify, &snd_pcm_notify_list, list) - notify->n_register(pcm); + pcm_call_notify(pcm, n_register); unlock: mutex_unlock(®ister_mutex); @@ -1118,7 +1127,6 @@ static int snd_pcm_dev_register(struct snd_device *device) static int snd_pcm_dev_disconnect(struct snd_device *device) { struct snd_pcm *pcm = device->device_data; - struct snd_pcm_notify *notify; struct snd_pcm_substream *substream; int cidx; @@ -1138,8 +1146,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) } } if (!pcm->internal) { - list_for_each_entry(notify, &snd_pcm_notify_list, list) - notify->n_disconnect(pcm); + pcm_call_notify(pcm, n_disconnect); } for (cidx = 0; cidx < 2; cidx++) { if (!pcm->internal) @@ -1151,6 +1158,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) return 0; } +#if IS_ENABLED(CONFIG_SND_PCM_OSS) /** * snd_pcm_notify - Add/remove the notify list * @notify: PCM notify list @@ -1183,6 +1191,7 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree) return 0; } EXPORT_SYMBOL(snd_pcm_notify); +#endif /* CONFIG_SND_PCM_OSS */ #ifdef CONFIG_SND_PROC_FS /* diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 1f64ab0c2a95..10f537f4d735 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -27,17 +27,13 @@ static int snd_pcm_ioctl_delay_compat(struct snd_pcm_substream *substream, s32 __user *src) { snd_pcm_sframes_t delay; - mm_segment_t fs; - int err; - fs = snd_enter_user(); - err = snd_pcm_delay(substream, &delay); - snd_leave_user(fs); - if (err < 0) - return err; + delay = snd_pcm_delay(substream); + if (delay < 0) + return delay; if (put_user(delay, src)) return -EFAULT; - return err; + return 0; } static int snd_pcm_ioctl_rewind_compat(struct snd_pcm_substream *substream, @@ -680,6 +676,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l case SNDRV_PCM_IOCTL_INFO: case SNDRV_PCM_IOCTL_TSTAMP: case SNDRV_PCM_IOCTL_TTSTAMP: + case SNDRV_PCM_IOCTL_USER_PVERSION: case SNDRV_PCM_IOCTL_HWSYNC: case SNDRV_PCM_IOCTL_PREPARE: case SNDRV_PCM_IOCTL_RESET: diff --git a/sound/core/pcm_drm_eld.c b/sound/core/pcm_drm_eld.c index e70379fb63d0..9881d087756f 100644 --- a/sound/core/pcm_drm_eld.c +++ b/sound/core/pcm_drm_eld.c @@ -29,13 +29,13 @@ static int eld_limit_rates(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *r = hw_param_interval(params, rule->var); - struct snd_interval *c; + const struct snd_interval *c; unsigned int rate_mask = 7, i; const u8 *sad, *eld = rule->private; sad = drm_eld_sad(eld); if (sad) { - c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + c = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3) { unsigned max_channels = sad_max_channels(sad); @@ -57,7 +57,7 @@ static int eld_limit_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { struct snd_interval *c = hw_param_interval(params, rule->var); - struct snd_interval *r; + const struct snd_interval *r; struct snd_interval t = { .min = 1, .max = 2, .integer = 1, }; unsigned int i; const u8 *sad, *eld = rule->private; @@ -67,7 +67,7 @@ static int eld_limit_channels(struct snd_pcm_hw_params *params, unsigned int rate_mask = 0; /* Convert the rate interval to a mask */ - r = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + r = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); for (i = 0; i < ARRAY_SIZE(eld_rates); i++) if (r->min <= eld_rates[i] && r->max >= eld_rates[i]) rate_mask |= BIT(i); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 877176067072..a93a4235a332 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -33,6 +33,8 @@ #include <sound/pcm_params.h> #include <sound/timer.h> +#include "pcm_local.h" + #ifdef CONFIG_SND_PCM_XRUN_DEBUG #define CREATE_TRACE_POINTS #include "pcm_trace.h" @@ -40,8 +42,12 @@ #define trace_hwptr(substream, pos, in_interrupt) #define trace_xrun(substream) #define trace_hw_ptr_error(substream, reason) +#define trace_applptr(substream, prev, curr) #endif +static int fill_silence_frames(struct snd_pcm_substream *substream, + snd_pcm_uframes_t off, snd_pcm_uframes_t frames); + /* * fill ring buffer with silence * runtime->silence_start: starting pointer to silence area @@ -55,18 +61,20 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t frames, ofs, transfer; + int err; if (runtime->silence_size < runtime->boundary) { snd_pcm_sframes_t noise_dist, n; - if (runtime->silence_start != runtime->control->appl_ptr) { - n = runtime->control->appl_ptr - runtime->silence_start; + snd_pcm_uframes_t appl_ptr = READ_ONCE(runtime->control->appl_ptr); + if (runtime->silence_start != appl_ptr) { + n = appl_ptr - runtime->silence_start; if (n < 0) n += runtime->boundary; if ((snd_pcm_uframes_t)n < runtime->silence_filled) runtime->silence_filled -= n; else runtime->silence_filled = 0; - runtime->silence_start = runtime->control->appl_ptr; + runtime->silence_start = appl_ptr; } if (runtime->silence_filled >= runtime->buffer_size) return; @@ -107,33 +115,8 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram ofs = runtime->silence_start % runtime->buffer_size; while (frames > 0) { transfer = ofs + frames > runtime->buffer_size ? runtime->buffer_size - ofs : frames; - if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || - runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) { - if (substream->ops->silence) { - int err; - err = substream->ops->silence(substream, -1, ofs, transfer); - snd_BUG_ON(err < 0); - } else { - char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); - snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); - } - } else { - unsigned int c; - unsigned int channels = runtime->channels; - if (substream->ops->silence) { - for (c = 0; c < channels; ++c) { - int err; - err = substream->ops->silence(substream, c, ofs, transfer); - snd_BUG_ON(err < 0); - } - } else { - size_t dma_csize = runtime->dma_bytes / channels; - for (c = 0; c < channels; ++c) { - char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); - snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); - } - } - } + err = fill_silence_frames(substream, ofs, transfer); + snd_BUG_ON(err < 0); runtime->silence_filled += transfer; frames -= transfer; ofs = 0; @@ -508,7 +491,6 @@ void snd_pcm_set_ops(struct snd_pcm *pcm, int direction, for (substream = stream->substream; substream != NULL; substream = substream->next) substream->ops = ops; } - EXPORT_SYMBOL(snd_pcm_set_ops); /** @@ -526,7 +508,6 @@ void snd_pcm_set_sync(struct snd_pcm_substream *substream) runtime->sync.id32[2] = -1; runtime->sync.id32[3] = -1; } - EXPORT_SYMBOL(snd_pcm_set_sync); /* @@ -643,7 +624,6 @@ int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) } return changed; } - EXPORT_SYMBOL(snd_interval_refine); static int snd_interval_refine_first(struct snd_interval *i) @@ -906,7 +886,6 @@ int snd_interval_ratnum(struct snd_interval *i, } return err; } - EXPORT_SYMBOL(snd_interval_ratnum); /** @@ -1044,7 +1023,6 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, } return snd_interval_refine(i, &list_range); } - EXPORT_SYMBOL(snd_interval_list); /** @@ -1183,7 +1161,6 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime, unsigned int cond, va_end(args); return 0; } - EXPORT_SYMBOL(snd_pcm_hw_rule_add); /** @@ -1247,7 +1224,6 @@ int snd_pcm_hw_constraint_integer(struct snd_pcm_runtime *runtime, snd_pcm_hw_pa struct snd_pcm_hw_constraints *constrs = &runtime->hw_constraints; return snd_interval_setinteger(constrs_interval(constrs, var)); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); /** @@ -1273,7 +1249,6 @@ int snd_pcm_hw_constraint_minmax(struct snd_pcm_runtime *runtime, snd_pcm_hw_par t.integer = 0; return snd_interval_refine(constrs_interval(constrs, var), &t); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, @@ -1304,7 +1279,6 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_list, (void *)l, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_list); static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, @@ -1371,7 +1345,6 @@ int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_ratnums, (void *)r, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, @@ -1406,7 +1379,6 @@ int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_ratdens, (void *)r, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, @@ -1415,7 +1387,8 @@ static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, unsigned int l = (unsigned long) rule->private; int width = l & 0xffff; unsigned int msbits = l >> 16; - struct snd_interval *i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + const struct snd_interval *i = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); if (!snd_interval_single(i)) return 0; @@ -1452,7 +1425,6 @@ int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, (void*) l, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, @@ -1480,7 +1452,6 @@ int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_step, (void *) step, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_step); static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) @@ -1511,7 +1482,6 @@ int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime, snd_pcm_hw_rule_pow2, NULL, var, -1); } - EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params, @@ -1570,7 +1540,6 @@ void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) _snd_pcm_hw_param_any(params, k); params->info = ~0U; } - EXPORT_SYMBOL(_snd_pcm_hw_params_any); /** @@ -1603,7 +1572,6 @@ int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, } return -EINVAL; } - EXPORT_SYMBOL(snd_pcm_hw_param_value); void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, @@ -1621,7 +1589,6 @@ void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_BUG(); } } - EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, @@ -1668,7 +1635,6 @@ int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, } return snd_pcm_hw_param_value(params, var, dir); } - EXPORT_SYMBOL(snd_pcm_hw_param_first); static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, @@ -1715,48 +1681,8 @@ int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, } return snd_pcm_hw_param_value(params, var, dir); } - EXPORT_SYMBOL(snd_pcm_hw_param_last); -/** - * snd_pcm_hw_param_choose - choose a configuration defined by @params - * @pcm: PCM instance - * @params: the hw_params instance - * - * Choose one configuration from configuration space defined by @params. - * The configuration chosen is that obtained fixing in this order: - * first access, first format, first subformat, min channels, - * min rate, min period time, max buffer size, min tick time - * - * Return: Zero if successful, or a negative error code on failure. - */ -int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, - struct snd_pcm_hw_params *params) -{ - static int vars[] = { - SNDRV_PCM_HW_PARAM_ACCESS, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_SUBFORMAT, - SNDRV_PCM_HW_PARAM_CHANNELS, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_PERIOD_TIME, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - SNDRV_PCM_HW_PARAM_TICK_TIME, - -1 - }; - int err, *v; - - for (v = vars; *v != -1; v++) { - if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) - err = snd_pcm_hw_param_first(pcm, params, *v, NULL); - else - err = snd_pcm_hw_param_last(pcm, params, *v, NULL); - if (snd_BUG_ON(err < 0)) - return err; - } - return 0; -} - static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, void *arg) { @@ -1843,8 +1769,6 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { switch (cmd) { - case SNDRV_PCM_IOCTL1_INFO: - return 0; case SNDRV_PCM_IOCTL1_RESET: return snd_pcm_lib_ioctl_reset(substream, arg); case SNDRV_PCM_IOCTL1_CHANNEL_INFO: @@ -1854,7 +1778,6 @@ int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream, } return -ENXIO; } - EXPORT_SYMBOL(snd_pcm_lib_ioctl); /** @@ -1890,7 +1813,6 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) kill_fasync(&runtime->fasync, SIGIO, POLL_IN); snd_pcm_stream_unlock_irqrestore(substream, flags); } - EXPORT_SYMBOL(snd_pcm_period_elapsed); /* @@ -1985,129 +1907,147 @@ static int wait_for_avail(struct snd_pcm_substream *substream, return err; } -static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t frames) +typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes); + +typedef int (*pcm_copy_f)(struct snd_pcm_substream *, snd_pcm_uframes_t, void *, + snd_pcm_uframes_t, snd_pcm_uframes_t, pcm_transfer_f); + +/* calculate the target DMA-buffer position to be written/read */ +static void *get_dma_ptr(struct snd_pcm_runtime *runtime, + int channel, unsigned long hwoff) { - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); - if (substream->ops->copy) { - if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) - return err; - } else { - char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); - if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) - return -EFAULT; - } + return runtime->dma_area + hwoff + + channel * (runtime->dma_bytes / runtime->channels); +} + +/* default copy_user ops for write; used for both interleaved and non- modes */ +static int default_write_copy(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff), + (void __user *)buf, bytes)) + return -EFAULT; return 0; } - -typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t size); -static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, - unsigned long data, - snd_pcm_uframes_t size, - int nonblock, - transfer_f transfer) +/* default copy_kernel ops for write */ +static int default_write_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + memcpy(get_dma_ptr(substream->runtime, channel, hwoff), buf, bytes); + return 0; +} + +/* fill silence instead of copy data; called as a transfer helper + * from __snd_pcm_lib_write() or directly from noninterleaved_copy() when + * a NULL buffer is passed + */ +static int fill_silence(struct snd_pcm_substream *substream, int channel, + unsigned long hwoff, void *buf, unsigned long bytes) { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t xfer = 0; - snd_pcm_uframes_t offset = 0; - snd_pcm_uframes_t avail; - int err = 0; - if (size == 0) + if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return 0; + if (substream->ops->fill_silence) + return substream->ops->fill_silence(substream, channel, + hwoff, bytes); - snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_RUNNING: - case SNDRV_PCM_STATE_PAUSED: - break; - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; - goto _end_unlock; - default: - err = -EBADFD; - goto _end_unlock; - } + snd_pcm_format_set_silence(runtime->format, + get_dma_ptr(runtime, channel, hwoff), + bytes_to_samples(runtime, bytes)); + return 0; +} - runtime->twake = runtime->control->avail_min ? : 1; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) - snd_pcm_update_hw_ptr(substream); - avail = snd_pcm_playback_avail(runtime); - while (size > 0) { - snd_pcm_uframes_t frames, appl_ptr, appl_ofs; - snd_pcm_uframes_t cont; - if (!avail) { - if (nonblock) { - err = -EAGAIN; - goto _end_unlock; - } - runtime->twake = min_t(snd_pcm_uframes_t, size, - runtime->control->avail_min ? : 1); - err = wait_for_avail(substream, &avail); - if (err < 0) - goto _end_unlock; - } - frames = size > avail ? avail : size; - cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; - if (frames > cont) - frames = cont; - if (snd_BUG_ON(!frames)) { - runtime->twake = 0; - snd_pcm_stream_unlock_irq(substream); - return -EINVAL; - } - appl_ptr = runtime->control->appl_ptr; - appl_ofs = appl_ptr % runtime->buffer_size; - snd_pcm_stream_unlock_irq(substream); - err = transfer(substream, appl_ofs, data, offset, frames); - snd_pcm_stream_lock_irq(substream); - if (err < 0) - goto _end_unlock; - switch (runtime->status->state) { - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; - goto _end_unlock; - default: - break; - } - appl_ptr += frames; - if (appl_ptr >= runtime->boundary) - appl_ptr -= runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - if (substream->ops->ack) - substream->ops->ack(substream); +/* default copy_user ops for read; used for both interleaved and non- modes */ +static int default_read_copy(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + if (copy_to_user((void __user *)buf, + get_dma_ptr(substream->runtime, channel, hwoff), + bytes)) + return -EFAULT; + return 0; +} - offset += frames; - size -= frames; - xfer += frames; - avail -= frames; - if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && - snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { - err = snd_pcm_start(substream); - if (err < 0) - goto _end_unlock; - } +/* default copy_kernel ops for read */ +static int default_read_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) +{ + memcpy(buf, get_dma_ptr(substream->runtime, channel, hwoff), bytes); + return 0; +} + +/* call transfer function with the converted pointers and sizes; + * for interleaved mode, it's one shot for all samples + */ +static int interleaved_copy(struct snd_pcm_substream *substream, + snd_pcm_uframes_t hwoff, void *data, + snd_pcm_uframes_t off, + snd_pcm_uframes_t frames, + pcm_transfer_f transfer) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* convert to bytes */ + hwoff = frames_to_bytes(runtime, hwoff); + off = frames_to_bytes(runtime, off); + frames = frames_to_bytes(runtime, frames); + return transfer(substream, 0, hwoff, data + off, frames); +} + +/* call transfer function with the converted pointers and sizes for each + * non-interleaved channel; when buffer is NULL, silencing instead of copying + */ +static int noninterleaved_copy(struct snd_pcm_substream *substream, + snd_pcm_uframes_t hwoff, void *data, + snd_pcm_uframes_t off, + snd_pcm_uframes_t frames, + pcm_transfer_f transfer) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int channels = runtime->channels; + void **bufs = data; + int c, err; + + /* convert to bytes; note that it's not frames_to_bytes() here. + * in non-interleaved mode, we copy for each channel, thus + * each copy is n_samples bytes x channels = whole frames. + */ + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + hwoff = samples_to_bytes(runtime, hwoff); + for (c = 0; c < channels; ++c, ++bufs) { + if (!data || !*bufs) + err = fill_silence(substream, c, hwoff, NULL, frames); + else + err = transfer(substream, c, hwoff, *bufs + off, + frames); + if (err < 0) + return err; } - _end_unlock: - runtime->twake = 0; - if (xfer > 0 && err >= 0) - snd_pcm_update_state(substream, runtime); - snd_pcm_stream_unlock_irq(substream); - return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; + return 0; +} + +/* fill silence on the given buffer position; + * called from snd_pcm_playback_silence() + */ +static int fill_silence_frames(struct snd_pcm_substream *substream, + snd_pcm_uframes_t off, snd_pcm_uframes_t frames) +{ + if (substream->runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED || + substream->runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) + return interleaved_copy(substream, off, NULL, 0, frames, + fill_silence); + else + return noninterleaved_copy(substream, off, NULL, 0, frames, + fill_silence); } /* sanity-check for read/write methods */ @@ -2117,164 +2057,137 @@ static int pcm_sanity_check(struct snd_pcm_substream *substream) if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; runtime = substream->runtime; - if (snd_BUG_ON(!substream->ops->copy && !runtime->dma_area)) + if (snd_BUG_ON(!substream->ops->copy_user && !runtime->dma_area)) return -EINVAL; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; return 0; } -snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size) +static int pcm_accessible_state(struct snd_pcm_runtime *runtime) { - struct snd_pcm_runtime *runtime; - int nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - runtime = substream->runtime; - nonblock = !!(substream->f_flags & O_NONBLOCK); - - if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && - runtime->channels > 1) - return -EINVAL; - return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, - snd_pcm_lib_write_transfer); + switch (runtime->status->state) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_RUNNING: + case SNDRV_PCM_STATE_PAUSED: + return 0; + case SNDRV_PCM_STATE_XRUN: + return -EPIPE; + case SNDRV_PCM_STATE_SUSPENDED: + return -ESTRPIPE; + default: + return -EBADFD; + } } -EXPORT_SYMBOL(snd_pcm_lib_write); - -static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t frames) +/* update to the given appl_ptr and call ack callback if needed; + * when an error is returned, take back to the original value + */ +int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, + snd_pcm_uframes_t appl_ptr) { struct snd_pcm_runtime *runtime = substream->runtime; - int err; - void __user **bufs = (void __user **)data; - int channels = runtime->channels; - int c; - if (substream->ops->copy) { - if (snd_BUG_ON(!substream->ops->silence)) - return -EINVAL; - for (c = 0; c < channels; ++c, ++bufs) { - if (*bufs == NULL) { - if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) - return err; - } else { - char __user *buf = *bufs + samples_to_bytes(runtime, off); - if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) - return err; - } - } - } else { - /* default transfer behaviour */ - size_t dma_csize = runtime->dma_bytes / channels; - for (c = 0; c < channels; ++c, ++bufs) { - char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); - if (*bufs == NULL) { - snd_pcm_format_set_silence(runtime->format, hwbuf, frames); - } else { - char __user *buf = *bufs + samples_to_bytes(runtime, off); - if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames))) - return -EFAULT; - } + snd_pcm_uframes_t old_appl_ptr = runtime->control->appl_ptr; + int ret; + + if (old_appl_ptr == appl_ptr) + return 0; + + runtime->control->appl_ptr = appl_ptr; + if (substream->ops->ack) { + ret = substream->ops->ack(substream); + if (ret < 0) { + runtime->control->appl_ptr = old_appl_ptr; + return ret; } } + + trace_applptr(substream, old_appl_ptr, appl_ptr); + return 0; } - -snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, - void __user **bufs, - snd_pcm_uframes_t frames) + +/* the common loop for read/write data */ +snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, + void *data, bool interleaved, + snd_pcm_uframes_t size, bool in_kernel) { - struct snd_pcm_runtime *runtime; - int nonblock; + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_uframes_t xfer = 0; + snd_pcm_uframes_t offset = 0; + snd_pcm_uframes_t avail; + pcm_copy_f writer; + pcm_transfer_f transfer; + bool nonblock; + bool is_playback; int err; err = pcm_sanity_check(substream); if (err < 0) return err; - runtime = substream->runtime; - nonblock = !!(substream->f_flags & O_NONBLOCK); - - if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) - return -EINVAL; - return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames, - nonblock, snd_pcm_lib_writev_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_writev); -static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); - if (substream->ops->copy) { - if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) - return err; + is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + if (interleaved) { + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && + runtime->channels > 1) + return -EINVAL; + writer = interleaved_copy; } else { - char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); - if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) - return -EFAULT; + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + writer = noninterleaved_copy; } - return 0; -} -static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, - unsigned long data, - snd_pcm_uframes_t size, - int nonblock, - transfer_f transfer) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t xfer = 0; - snd_pcm_uframes_t offset = 0; - snd_pcm_uframes_t avail; - int err = 0; + if (!data) { + if (is_playback) + transfer = fill_silence; + else + return -EINVAL; + } else if (in_kernel) { + if (substream->ops->copy_kernel) + transfer = substream->ops->copy_kernel; + else + transfer = is_playback ? + default_write_copy_kernel : default_read_copy_kernel; + } else { + if (substream->ops->copy_user) + transfer = (pcm_transfer_f)substream->ops->copy_user; + else + transfer = is_playback ? + default_write_copy : default_read_copy; + } if (size == 0) return 0; + nonblock = !!(substream->f_flags & O_NONBLOCK); + snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - if (size >= runtime->start_threshold) { - err = snd_pcm_start(substream); - if (err < 0) - goto _end_unlock; - } - break; - case SNDRV_PCM_STATE_DRAINING: - case SNDRV_PCM_STATE_RUNNING: - case SNDRV_PCM_STATE_PAUSED: - break; - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; - goto _end_unlock; - default: - err = -EBADFD; + err = pcm_accessible_state(runtime); + if (err < 0) goto _end_unlock; + + if (!is_playback && + runtime->status->state == SNDRV_PCM_STATE_PREPARED && + size >= runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; } runtime->twake = runtime->control->avail_min ? : 1; if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) snd_pcm_update_hw_ptr(substream); - avail = snd_pcm_capture_avail(runtime); + if (is_playback) + avail = snd_pcm_playback_avail(runtime); + else + avail = snd_pcm_capture_avail(runtime); while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t cont; if (!avail) { - if (runtime->status->state == - SNDRV_PCM_STATE_DRAINING) { + if (!is_playback && + runtime->status->state == SNDRV_PCM_STATE_DRAINING) { snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); goto _end_unlock; } @@ -2291,7 +2204,9 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, continue; /* draining */ } frames = size > avail ? avail : size; - cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; + appl_ptr = READ_ONCE(runtime->control->appl_ptr); + appl_ofs = appl_ptr % runtime->buffer_size; + cont = runtime->buffer_size - appl_ofs; if (frames > cont) frames = cont; if (snd_BUG_ON(!frames)) { @@ -2299,34 +2214,33 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, snd_pcm_stream_unlock_irq(substream); return -EINVAL; } - appl_ptr = runtime->control->appl_ptr; - appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); - err = transfer(substream, appl_ofs, data, offset, frames); + err = writer(substream, appl_ofs, data, offset, frames, + transfer); snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end_unlock; - switch (runtime->status->state) { - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - goto _end_unlock; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; + err = pcm_accessible_state(runtime); + if (err < 0) goto _end_unlock; - default: - break; - } appl_ptr += frames; if (appl_ptr >= runtime->boundary) appl_ptr -= runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - if (substream->ops->ack) - substream->ops->ack(substream); + err = pcm_lib_apply_appl_ptr(substream, appl_ptr); + if (err < 0) + goto _end_unlock; offset += frames; size -= frames; xfer += frames; avail -= frames; + if (is_playback && + runtime->status->state == SNDRV_PCM_STATE_PREPARED && + snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; + } } _end_unlock: runtime->twake = 0; @@ -2335,83 +2249,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, snd_pcm_stream_unlock_irq(substream); return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } - -snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size) -{ - struct snd_pcm_runtime *runtime; - int nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - runtime = substream->runtime; - nonblock = !!(substream->f_flags & O_NONBLOCK); - if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) - return -EINVAL; - return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_read); - -static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - unsigned long data, unsigned int off, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - void __user **bufs = (void __user **)data; - int channels = runtime->channels; - int c; - if (substream->ops->copy) { - for (c = 0; c < channels; ++c, ++bufs) { - char __user *buf; - if (*bufs == NULL) - continue; - buf = *bufs + samples_to_bytes(runtime, off); - if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) - return err; - } - } else { - snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; - for (c = 0; c < channels; ++c, ++bufs) { - char *hwbuf; - char __user *buf; - if (*bufs == NULL) - continue; - - hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff); - buf = *bufs + samples_to_bytes(runtime, off); - if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames))) - return -EFAULT; - } - } - return 0; -} - -snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, - void __user **bufs, - snd_pcm_uframes_t frames) -{ - struct snd_pcm_runtime *runtime; - int nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_OPEN) - return -EBADFD; - - nonblock = !!(substream->f_flags & O_NONBLOCK); - if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) - return -EINVAL; - return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_readv); +EXPORT_SYMBOL(__snd_pcm_lib_xfer); /* * standard channel mapping helpers diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h new file mode 100644 index 000000000000..16f254732b2a --- /dev/null +++ b/sound/core/pcm_local.h @@ -0,0 +1,50 @@ +/* + * pcm_local.h - a local header file for snd-pcm module. + * + * Copyright (c) Takashi Sakamoto <o-takashi@sakamocchi.jp> + * + * Licensed under the terms of the GNU General Public License, version 2. + */ + +#ifndef __SOUND_CORE_PCM_LOCAL_H +#define __SOUND_CORE_PCM_LOCAL_H + +extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; + +void snd_interval_mul(const struct snd_interval *a, + const struct snd_interval *b, struct snd_interval *c); +void snd_interval_div(const struct snd_interval *a, + const struct snd_interval *b, struct snd_interval *c); +void snd_interval_muldivk(const struct snd_interval *a, + const struct snd_interval *b, + unsigned int k, struct snd_interval *c); +void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, + const struct snd_interval *b, struct snd_interval *c); + +int snd_pcm_hw_constraints_init(struct snd_pcm_substream *substream); +int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream); + +int snd_pcm_hw_constraint_mask(struct snd_pcm_runtime *runtime, + snd_pcm_hw_param_t var, u_int32_t mask); + +int pcm_lib_apply_appl_ptr(struct snd_pcm_substream *substream, + snd_pcm_uframes_t appl_ptr); +int snd_pcm_update_state(struct snd_pcm_substream *substream, + struct snd_pcm_runtime *runtime); +int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream); + +void snd_pcm_playback_silence(struct snd_pcm_substream *substream, + snd_pcm_uframes_t new_hw_ptr); + +#ifdef CONFIG_SND_PCM_TIMER +void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream); +void snd_pcm_timer_init(struct snd_pcm_substream *substream); +void snd_pcm_timer_done(struct snd_pcm_substream *substream); +#else +static inline void +snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {} +static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {} +static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {} +#endif + +#endif /* __SOUND_CORE_PCM_LOCAL_H */ diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index b45f6aa32264..ae33e456708c 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -120,7 +120,6 @@ int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm) snd_pcm_lib_preallocate_free(substream); return 0; } - EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); #ifdef CONFIG_SND_VERBOSE_PROCFS @@ -263,7 +262,6 @@ int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream, substream->dma_buffer.dev.dev = data; return snd_pcm_lib_preallocate_pages1(substream, size, max); } - EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); /** @@ -292,7 +290,6 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm, return err; return 0; } - EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); #ifdef CONFIG_SND_DMA_SGBUF @@ -314,7 +311,6 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne return NULL; return sgbuf->page_table[idx]; } - EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); #endif /* CONFIG_SND_DMA_SGBUF */ @@ -370,7 +366,6 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) runtime->dma_bytes = size; return 1; /* area was changed */ } - EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); /** @@ -398,7 +393,6 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream) snd_pcm_set_runtime_buffer(substream, NULL); return 0; } - EXPORT_SYMBOL(snd_pcm_lib_free_pages); int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream, diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 53dc37357bca..9be81025372f 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -23,6 +23,9 @@ #include <linux/export.h> #include <sound/core.h> #include <sound/pcm.h> + +#include "pcm_local.h" + #define SND_PCM_FORMAT_UNKNOWN (-1) /* NOTE: "signed" prefix must be given below since the default char is @@ -245,7 +248,6 @@ int snd_pcm_format_signed(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_signed); /** @@ -264,7 +266,6 @@ int snd_pcm_format_unsigned(snd_pcm_format_t format) return val; return !val; } - EXPORT_SYMBOL(snd_pcm_format_unsigned); /** @@ -277,7 +278,6 @@ int snd_pcm_format_linear(snd_pcm_format_t format) { return snd_pcm_format_signed(format) >= 0; } - EXPORT_SYMBOL(snd_pcm_format_linear); /** @@ -296,7 +296,6 @@ int snd_pcm_format_little_endian(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_little_endian); /** @@ -315,7 +314,6 @@ int snd_pcm_format_big_endian(snd_pcm_format_t format) return val; return !val; } - EXPORT_SYMBOL(snd_pcm_format_big_endian); /** @@ -334,7 +332,6 @@ int snd_pcm_format_width(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_width); /** @@ -353,7 +350,6 @@ int snd_pcm_format_physical_width(snd_pcm_format_t format) return -EINVAL; return val; } - EXPORT_SYMBOL(snd_pcm_format_physical_width); /** @@ -371,7 +367,6 @@ ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples) return -EINVAL; return samples * phys_width / 8; } - EXPORT_SYMBOL(snd_pcm_format_size); /** @@ -388,7 +383,6 @@ const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format) return NULL; return pcm_formats[(INT)format].silence; } - EXPORT_SYMBOL(snd_pcm_format_silence_64); /** @@ -459,7 +453,6 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int #endif return 0; } - EXPORT_SYMBOL(snd_pcm_format_set_silence); /** @@ -488,7 +481,6 @@ int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime) } return 0; } - EXPORT_SYMBOL(snd_pcm_limit_hw_rates); /** diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index faa2e2be6f2e..b3d5bed75029 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -37,6 +37,18 @@ #include <sound/minors.h> #include <linux/uio.h> +#include "pcm_local.h" + +#ifdef CONFIG_SND_DEBUG +#define CREATE_TRACE_POINTS +#include "pcm_param_trace.h" +#else +#define trace_hw_mask_param_enabled() 0 +#define trace_hw_interval_param_enabled() 0 +#define trace_hw_mask_param(substream, type, index, prev, curr) +#define trace_hw_interval_param(substream, type, index, prev, curr) +#endif + /* * Compatibility */ @@ -181,20 +193,6 @@ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, } EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore); -static inline mm_segment_t snd_enter_user(void) -{ - mm_segment_t fs = get_fs(); - set_fs(get_ds()); - return fs; -} - -static inline void snd_leave_user(mm_segment_t fs) -{ - set_fs(fs); -} - - - int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) { struct snd_pcm_runtime *runtime; @@ -214,11 +212,7 @@ int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info) info->subdevices_avail = pstr->substream_count - pstr->substream_opened; strlcpy(info->subname, substream->name, sizeof(info->subname)); runtime = substream->runtime; - /* AB: FIXME!!! This is definitely nonsense */ - if (runtime) { - info->sync = runtime->sync; - substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info); - } + return 0; } @@ -255,205 +249,268 @@ static bool hw_support_mmap(struct snd_pcm_substream *substream) return true; } -#undef RULES_DEBUG - -#ifdef RULES_DEBUG -#define HW_PARAM(v) [SNDRV_PCM_HW_PARAM_##v] = #v -static const char * const snd_pcm_hw_param_names[] = { - HW_PARAM(ACCESS), - HW_PARAM(FORMAT), - HW_PARAM(SUBFORMAT), - HW_PARAM(SAMPLE_BITS), - HW_PARAM(FRAME_BITS), - HW_PARAM(CHANNELS), - HW_PARAM(RATE), - HW_PARAM(PERIOD_TIME), - HW_PARAM(PERIOD_SIZE), - HW_PARAM(PERIOD_BYTES), - HW_PARAM(PERIODS), - HW_PARAM(BUFFER_TIME), - HW_PARAM(BUFFER_SIZE), - HW_PARAM(BUFFER_BYTES), - HW_PARAM(TICK_TIME), -}; -#endif - -int snd_pcm_hw_refine(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int constrain_mask_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { + struct snd_pcm_hw_constraints *constrs = + &substream->runtime->hw_constraints; + struct snd_mask *m; unsigned int k; - struct snd_pcm_hardware *hw; - struct snd_interval *i = NULL; - struct snd_mask *m = NULL; - struct snd_pcm_hw_constraints *constrs = &substream->runtime->hw_constraints; - unsigned int rstamps[constrs->rules_num]; - unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; - unsigned int stamp = 2; - int changed, again; - - params->info = 0; - params->fifo_size = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) - params->msbits = 0; - if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { - params->rate_num = 0; - params->rate_den = 0; - } + struct snd_mask old_mask; + int changed; for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) { m = hw_param_mask(params, k); if (snd_mask_empty(m)) return -EINVAL; + + /* This parameter is not requested to change by a caller. */ if (!(params->rmask & (1 << k))) continue; -#ifdef RULES_DEBUG - pr_debug("%s = ", snd_pcm_hw_param_names[k]); - pr_cont("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); -#endif + + if (trace_hw_mask_param_enabled()) + old_mask = *m; + changed = snd_mask_refine(m, constrs_mask(constrs, k)); -#ifdef RULES_DEBUG - pr_cont("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]); -#endif - if (changed) - params->cmask |= 1 << k; if (changed < 0) return changed; + if (changed == 0) + continue; + + /* Set corresponding flag so that the caller gets it. */ + trace_hw_mask_param(substream, k, 0, &old_mask, m); + params->cmask |= 1 << k; } + return 0; +} + +static int constrain_interval_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_hw_constraints *constrs = + &substream->runtime->hw_constraints; + struct snd_interval *i; + unsigned int k; + struct snd_interval old_interval; + int changed; + for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) { i = hw_param_interval(params, k); if (snd_interval_empty(i)) return -EINVAL; + + /* This parameter is not requested to change by a caller. */ if (!(params->rmask & (1 << k))) continue; -#ifdef RULES_DEBUG - pr_debug("%s = ", snd_pcm_hw_param_names[k]); - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - pr_cont(" -> "); -#endif + + if (trace_hw_interval_param_enabled()) + old_interval = *i; + changed = snd_interval_refine(i, constrs_interval(constrs, k)); -#ifdef RULES_DEBUG - if (i->empty) - pr_cont("empty\n"); - else - pr_cont("%c%u %u%c\n", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); -#endif - if (changed) - params->cmask |= 1 << k; if (changed < 0) return changed; + if (changed == 0) + continue; + + /* Set corresponding flag so that the caller gets it. */ + trace_hw_interval_param(substream, k, 0, &old_interval, i); + params->cmask |= 1 << k; } + return 0; +} + +static int constrain_params_by_rules(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_hw_constraints *constrs = + &substream->runtime->hw_constraints; + unsigned int k; + unsigned int rstamps[constrs->rules_num]; + unsigned int vstamps[SNDRV_PCM_HW_PARAM_LAST_INTERVAL + 1]; + unsigned int stamp; + struct snd_pcm_hw_rule *r; + unsigned int d; + struct snd_mask old_mask; + struct snd_interval old_interval; + bool again; + int changed; + + /* + * Each application of rule has own sequence number. + * + * Each member of 'rstamps' array represents the sequence number of + * recent application of corresponding rule. + */ for (k = 0; k < constrs->rules_num; k++) rstamps[k] = 0; - for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) + + /* + * Each member of 'vstamps' array represents the sequence number of + * recent application of rule in which corresponding parameters were + * changed. + * + * In initial state, elements corresponding to parameters requested by + * a caller is 1. For unrequested parameters, corresponding members + * have 0 so that the parameters are never changed anymore. + */ + for (k = 0; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; - do { - again = 0; - for (k = 0; k < constrs->rules_num; k++) { - struct snd_pcm_hw_rule *r = &constrs->rules[k]; - unsigned int d; - int doit = 0; - if (r->cond && !(r->cond & params->flags)) - continue; - for (d = 0; r->deps[d] >= 0; d++) { - if (vstamps[r->deps[d]] > rstamps[k]) { - doit = 1; - break; - } - } - if (!doit) - continue; -#ifdef RULES_DEBUG - pr_debug("Rule %d [%p]: ", k, r->func); - if (r->var >= 0) { - pr_cont("%s = ", snd_pcm_hw_param_names[r->var]); - if (hw_is_mask(r->var)) { - m = hw_param_mask(params, r->var); - pr_cont("%x", *m->bits); - } else { - i = hw_param_interval(params, r->var); - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - } - } -#endif - changed = r->func(params, r); -#ifdef RULES_DEBUG - if (r->var >= 0) { - pr_cont(" -> "); - if (hw_is_mask(r->var)) - pr_cont("%x", *m->bits); - else { - if (i->empty) - pr_cont("empty"); - else - pr_cont("%c%u %u%c", - i->openmin ? '(' : '[', i->min, - i->max, i->openmax ? ')' : ']'); - } + + /* Due to the above design, actual sequence number starts at 2. */ + stamp = 2; +retry: + /* Apply all rules in order. */ + again = false; + for (k = 0; k < constrs->rules_num; k++) { + r = &constrs->rules[k]; + + /* + * Check condition bits of this rule. When the rule has + * some condition bits, parameter without the bits is + * never processed. SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP + * is an example of the condition bits. + */ + if (r->cond && !(r->cond & params->flags)) + continue; + + /* + * The 'deps' array includes maximum three dependencies + * to SNDRV_PCM_HW_PARAM_XXXs for this rule. The fourth + * member of this array is a sentinel and should be + * negative value. + * + * This rule should be processed in this time when dependent + * parameters were changed at former applications of the other + * rules. + */ + for (d = 0; r->deps[d] >= 0; d++) { + if (vstamps[r->deps[d]] > rstamps[k]) + break; + } + if (r->deps[d] < 0) + continue; + + if (trace_hw_mask_param_enabled()) { + if (hw_is_mask(r->var)) + old_mask = *hw_param_mask(params, r->var); + } + if (trace_hw_interval_param_enabled()) { + if (hw_is_interval(r->var)) + old_interval = *hw_param_interval(params, r->var); + } + + changed = r->func(params, r); + if (changed < 0) + return changed; + + /* + * When the parameter is changed, notify it to the caller + * by corresponding returned bit, then preparing for next + * iteration. + */ + if (changed && r->var >= 0) { + if (hw_is_mask(r->var)) { + trace_hw_mask_param(substream, r->var, + k + 1, &old_mask, + hw_param_mask(params, r->var)); } - pr_cont("\n"); -#endif - rstamps[k] = stamp; - if (changed && r->var >= 0) { - params->cmask |= (1 << r->var); - vstamps[r->var] = stamp; - again = 1; + if (hw_is_interval(r->var)) { + trace_hw_interval_param(substream, r->var, + k + 1, &old_interval, + hw_param_interval(params, r->var)); } - if (changed < 0) - return changed; - stamp++; + + params->cmask |= (1 << r->var); + vstamps[r->var] = stamp; + again = true; } - } while (again); + + rstamps[k] = stamp++; + } + + /* Iterate to evaluate all rules till no parameters are changed. */ + if (again) + goto retry; + + return 0; +} + +static int fixup_unreferenced_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + const struct snd_interval *i; + const struct snd_mask *m; + int err; + if (!params->msbits) { - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); if (snd_interval_single(i)) params->msbits = snd_interval_value(i); } if (!params->rate_den) { - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); if (snd_interval_single(i)) { params->rate_num = snd_interval_value(i); params->rate_den = 1; } } - hw = &substream->runtime->hw; + if (!params->fifo_size) { + m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); + i = hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS); + if (snd_mask_single(m) && snd_interval_single(i)) { + err = substream->ops->ioctl(substream, + SNDRV_PCM_IOCTL1_FIFO_SIZE, params); + if (err < 0) + return err; + } + } + if (!params->info) { - params->info = hw->info & ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | - SNDRV_PCM_INFO_DRAIN_TRIGGER); + params->info = substream->runtime->hw.info; + params->info &= ~(SNDRV_PCM_INFO_FIFO_IN_FRAMES | + SNDRV_PCM_INFO_DRAIN_TRIGGER); if (!hw_support_mmap(substream)) params->info &= ~(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID); } - if (!params->fifo_size) { - m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - if (snd_mask_min(m) == snd_mask_max(m) && - snd_interval_min(i) == snd_interval_max(i)) { - changed = substream->ops->ioctl(substream, - SNDRV_PCM_IOCTL1_FIFO_SIZE, params); - if (changed < 0) - return changed; - } + + return 0; +} + +int snd_pcm_hw_refine(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int err; + + params->info = 0; + params->fifo_size = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_SAMPLE_BITS)) + params->msbits = 0; + if (params->rmask & (1 << SNDRV_PCM_HW_PARAM_RATE)) { + params->rate_num = 0; + params->rate_den = 0; } + + err = constrain_mask_params(substream, params); + if (err < 0) + return err; + + err = constrain_interval_params(substream, params); + if (err < 0) + return err; + + err = constrain_params_by_rules(substream, params); + if (err < 0) + return err; + params->rmask = 0; + return 0; } - EXPORT_SYMBOL(snd_pcm_hw_refine); static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, @@ -467,11 +524,16 @@ static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, return PTR_ERR(params); err = snd_pcm_hw_refine(substream, params); - if (copy_to_user(_params, params, sizeof(*params))) { - if (!err) - err = -EFAULT; - } + if (err < 0) + goto end; + + err = fixup_unreferenced_params(substream, params); + if (err < 0) + goto end; + if (copy_to_user(_params, params, sizeof(*params))) + err = -EFAULT; +end: kfree(params); return err; } @@ -509,6 +571,70 @@ static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream, #endif } +/** + * snd_pcm_hw_param_choose - choose a configuration defined by @params + * @pcm: PCM instance + * @params: the hw_params instance + * + * Choose one configuration from configuration space defined by @params. + * The configuration chosen is that obtained fixing in this order: + * first access, first format, first subformat, min channels, + * min rate, min period time, max buffer size, min tick time + * + * Return: Zero if successful, or a negative error code on failure. + */ +static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params) +{ + static const int vars[] = { + SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_SUBFORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + SNDRV_PCM_HW_PARAM_TICK_TIME, + -1 + }; + const int *v; + struct snd_mask old_mask; + struct snd_interval old_interval; + int changed; + + for (v = vars; *v != -1; v++) { + /* Keep old parameter to trace. */ + if (trace_hw_mask_param_enabled()) { + if (hw_is_mask(*v)) + old_mask = *hw_param_mask(params, *v); + } + if (trace_hw_interval_param_enabled()) { + if (hw_is_interval(*v)) + old_interval = *hw_param_interval(params, *v); + } + if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) + changed = snd_pcm_hw_param_first(pcm, params, *v, NULL); + else + changed = snd_pcm_hw_param_last(pcm, params, *v, NULL); + if (snd_BUG_ON(changed < 0)) + return changed; + if (changed == 0) + continue; + + /* Trace the changed parameter. */ + if (hw_is_mask(*v)) { + trace_hw_mask_param(pcm, *v, 0, &old_mask, + hw_param_mask(params, *v)); + } + if (hw_is_interval(*v)) { + trace_hw_interval_param(pcm, *v, 0, &old_interval, + hw_param_interval(params, *v)); + } + } + + return 0; +} + static int snd_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -546,6 +672,10 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, if (err < 0) goto _error; + err = fixup_unreferenced_params(substream, params); + if (err < 0) + goto _error; + if (substream->ops->hw_params != NULL) { err = substream->ops->hw_params(substream, params); if (err < 0) @@ -621,11 +751,12 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, return PTR_ERR(params); err = snd_pcm_hw_params(substream, params); - if (copy_to_user(_params, params, sizeof(*params))) { - if (!err) - err = -EFAULT; - } + if (err < 0) + goto end; + if (copy_to_user(_params, params, sizeof(*params))) + err = -EFAULT; +end: kfree(params); return err; } @@ -1081,6 +1212,7 @@ static const struct action_ops snd_pcm_action_start = { * @substream: the PCM substream instance * * Return: Zero if successful, or a negative error code. + * The stream lock must be acquired before calling this function. */ int snd_pcm_start(struct snd_pcm_substream *substream) { @@ -1088,6 +1220,13 @@ int snd_pcm_start(struct snd_pcm_substream *substream) SNDRV_PCM_STATE_RUNNING); } +/* take the stream lock and start the streams */ +static int snd_pcm_start_lock_irq(struct snd_pcm_substream *substream) +{ + return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, + SNDRV_PCM_STATE_RUNNING); +} + /* * stop callbacks */ @@ -1139,7 +1278,6 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t state) { return snd_pcm_action(&snd_pcm_action_stop, substream, state); } - EXPORT_SYMBOL(snd_pcm_stop); /** @@ -1314,7 +1452,6 @@ int snd_pcm_suspend(struct snd_pcm_substream *substream) snd_pcm_stream_unlock_irqrestore(substream, flags); return err; } - EXPORT_SYMBOL(snd_pcm_suspend); /** @@ -1346,7 +1483,6 @@ int snd_pcm_suspend_all(struct snd_pcm *pcm) } return 0; } - EXPORT_SYMBOL(snd_pcm_suspend_all); /* resume */ @@ -1397,14 +1533,7 @@ static const struct action_ops snd_pcm_action_resume = { static int snd_pcm_resume(struct snd_pcm_substream *substream) { - struct snd_card *card = substream->pcm->card; - int res; - - snd_power_lock(card); - if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) - res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); - snd_power_unlock(card); - return res; + return snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0); } #else @@ -1423,17 +1552,9 @@ static int snd_pcm_resume(struct snd_pcm_substream *substream) */ static int snd_pcm_xrun(struct snd_pcm_substream *substream) { - struct snd_card *card = substream->pcm->card; struct snd_pcm_runtime *runtime = substream->runtime; int result; - snd_power_lock(card); - if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { - result = snd_power_wait(card, SNDRV_CTL_POWER_D0); - if (result < 0) - goto _unlock; - } - snd_pcm_stream_lock_irq(substream); switch (runtime->status->state) { case SNDRV_PCM_STATE_XRUN: @@ -1446,8 +1567,6 @@ static int snd_pcm_xrun(struct snd_pcm_substream *substream) result = -EBADFD; } snd_pcm_stream_unlock_irq(substream); - _unlock: - snd_power_unlock(card); return result; } @@ -1551,8 +1670,6 @@ static const struct action_ops snd_pcm_action_prepare = { static int snd_pcm_prepare(struct snd_pcm_substream *substream, struct file *file) { - int res; - struct snd_card *card = substream->pcm->card; int f_flags; if (file) @@ -1560,12 +1677,19 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream, else f_flags = substream->f_flags; - snd_power_lock(card); - if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) - res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, - substream, f_flags); - snd_power_unlock(card); - return res; + snd_pcm_stream_lock_irq(substream); + switch (substream->runtime->status->state) { + case SNDRV_PCM_STATE_PAUSED: + snd_pcm_pause(substream, 0); + /* fallthru */ + case SNDRV_PCM_STATE_SUSPENDED: + snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP); + break; + } + snd_pcm_stream_unlock_irq(substream); + + return snd_pcm_action_nonatomic(&snd_pcm_action_prepare, + substream, f_flags); } /* @@ -1662,15 +1786,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; - snd_power_lock(card); - if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) { - result = snd_power_wait(card, SNDRV_CTL_POWER_D0); - if (result < 0) { - snd_power_unlock(card); - return result; - } - } - if (file) { if (file->f_flags & O_NONBLOCK) nonblock = 1; @@ -1753,7 +1868,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream, unlock: snd_pcm_stream_unlock_irq(substream); up_read(&snd_pcm_link_rwsem); - snd_power_unlock(card); return result; } @@ -1773,8 +1887,7 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream) runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN || - runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED || - runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) + runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; snd_pcm_stream_lock_irq(substream); @@ -1940,7 +2053,8 @@ static int snd_pcm_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { unsigned int k; - struct snd_interval *i = hw_param_interval(params, rule->deps[0]); + const struct snd_interval *i = + hw_param_interval_c(params, rule->deps[0]); struct snd_mask m; struct snd_mask *mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); snd_mask_any(&m); @@ -1986,8 +2100,10 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params, #error "Change this table" #endif -static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100, - 48000, 64000, 88200, 96000, 176400, 192000 }; +static const unsigned int rates[] = { + 5512, 8000, 11025, 16000, 22050, 32000, 44100, + 48000, 64000, 88200, 96000, 176400, 192000 +}; const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = { .count = ARRAY_SIZE(rates), @@ -2250,7 +2366,6 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) } snd_pcm_detach_substream(substream); } - EXPORT_SYMBOL(snd_pcm_release_substream); int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, @@ -2292,7 +2407,6 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, snd_pcm_release_substream(substream); return err; } - EXPORT_SYMBOL(snd_pcm_open_substream); static int snd_pcm_open_file(struct file *file, @@ -2428,50 +2542,84 @@ static int snd_pcm_release(struct inode *inode, struct file *file) return 0; } -static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream, - snd_pcm_uframes_t frames) +/* check and update PCM state; return 0 or a negative error + * call this inside PCM lock + */ +static int do_pcm_hwsync(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_sframes_t appl_ptr; - snd_pcm_sframes_t ret; - snd_pcm_sframes_t hw_avail; - - if (frames == 0) - return 0; - - snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - break; + switch (substream->runtime->status->state) { case SNDRV_PCM_STATE_DRAINING: - case SNDRV_PCM_STATE_RUNNING: - if (snd_pcm_update_hw_ptr(substream) >= 0) - break; + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + return -EBADFD; /* Fall through */ - case SNDRV_PCM_STATE_XRUN: - ret = -EPIPE; - goto __end; + case SNDRV_PCM_STATE_RUNNING: + return snd_pcm_update_hw_ptr(substream); + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_PAUSED: + return 0; case SNDRV_PCM_STATE_SUSPENDED: - ret = -ESTRPIPE; - goto __end; + return -ESTRPIPE; + case SNDRV_PCM_STATE_XRUN: + return -EPIPE; default: - ret = -EBADFD; - goto __end; + return -EBADFD; } +} - hw_avail = snd_pcm_playback_hw_avail(runtime); - if (hw_avail <= 0) { - ret = 0; - goto __end; - } - if (frames > (snd_pcm_uframes_t)hw_avail) - frames = hw_avail; +/* increase the appl_ptr; returns the processed frames or a negative error */ +static snd_pcm_sframes_t forward_appl_ptr(struct snd_pcm_substream *substream, + snd_pcm_uframes_t frames, + snd_pcm_sframes_t avail) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + int ret; + + if (avail <= 0) + return 0; + if (frames > (snd_pcm_uframes_t)avail) + frames = avail; + appl_ptr = runtime->control->appl_ptr + frames; + if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) + appl_ptr -= runtime->boundary; + ret = pcm_lib_apply_appl_ptr(substream, appl_ptr); + return ret < 0 ? ret : frames; +} + +/* decrease the appl_ptr; returns the processed frames or a negative error */ +static snd_pcm_sframes_t rewind_appl_ptr(struct snd_pcm_substream *substream, + snd_pcm_uframes_t frames, + snd_pcm_sframes_t avail) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_sframes_t appl_ptr; + int ret; + + if (avail <= 0) + return 0; + if (frames > (snd_pcm_uframes_t)avail) + frames = avail; appl_ptr = runtime->control->appl_ptr - frames; if (appl_ptr < 0) appl_ptr += runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - ret = frames; - __end: + ret = pcm_lib_apply_appl_ptr(substream, appl_ptr); + return ret < 0 ? ret : frames; +} + +static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *substream, + snd_pcm_uframes_t frames) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_sframes_t ret; + + if (frames == 0) + return 0; + + snd_pcm_stream_lock_irq(substream); + ret = do_pcm_hwsync(substream); + if (!ret) + ret = rewind_appl_ptr(substream, frames, + snd_pcm_playback_hw_avail(runtime)); snd_pcm_stream_unlock_irq(substream); return ret; } @@ -2480,46 +2628,16 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_sframes_t appl_ptr; snd_pcm_sframes_t ret; - snd_pcm_sframes_t hw_avail; if (frames == 0) return 0; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_DRAINING: - break; - case SNDRV_PCM_STATE_RUNNING: - if (snd_pcm_update_hw_ptr(substream) >= 0) - break; - /* Fall through */ - case SNDRV_PCM_STATE_XRUN: - ret = -EPIPE; - goto __end; - case SNDRV_PCM_STATE_SUSPENDED: - ret = -ESTRPIPE; - goto __end; - default: - ret = -EBADFD; - goto __end; - } - - hw_avail = snd_pcm_capture_hw_avail(runtime); - if (hw_avail <= 0) { - ret = 0; - goto __end; - } - if (frames > (snd_pcm_uframes_t)hw_avail) - frames = hw_avail; - appl_ptr = runtime->control->appl_ptr - frames; - if (appl_ptr < 0) - appl_ptr += runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - ret = frames; - __end: + ret = do_pcm_hwsync(substream); + if (!ret) + ret = rewind_appl_ptr(substream, frames, + snd_pcm_capture_hw_avail(runtime)); snd_pcm_stream_unlock_irq(substream); return ret; } @@ -2528,47 +2646,16 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_sframes_t appl_ptr; snd_pcm_sframes_t ret; - snd_pcm_sframes_t avail; if (frames == 0) return 0; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_PAUSED: - break; - case SNDRV_PCM_STATE_DRAINING: - case SNDRV_PCM_STATE_RUNNING: - if (snd_pcm_update_hw_ptr(substream) >= 0) - break; - /* Fall through */ - case SNDRV_PCM_STATE_XRUN: - ret = -EPIPE; - goto __end; - case SNDRV_PCM_STATE_SUSPENDED: - ret = -ESTRPIPE; - goto __end; - default: - ret = -EBADFD; - goto __end; - } - - avail = snd_pcm_playback_avail(runtime); - if (avail <= 0) { - ret = 0; - goto __end; - } - if (frames > (snd_pcm_uframes_t)avail) - frames = avail; - appl_ptr = runtime->control->appl_ptr + frames; - if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) - appl_ptr -= runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - ret = frames; - __end: + ret = do_pcm_hwsync(substream); + if (!ret) + ret = forward_appl_ptr(substream, frames, + snd_pcm_playback_avail(runtime)); snd_pcm_stream_unlock_irq(substream); return ret; } @@ -2577,123 +2664,47 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_sframes_t appl_ptr; snd_pcm_sframes_t ret; - snd_pcm_sframes_t avail; if (frames == 0) return 0; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_DRAINING: - case SNDRV_PCM_STATE_PAUSED: - break; - case SNDRV_PCM_STATE_RUNNING: - if (snd_pcm_update_hw_ptr(substream) >= 0) - break; - /* Fall through */ - case SNDRV_PCM_STATE_XRUN: - ret = -EPIPE; - goto __end; - case SNDRV_PCM_STATE_SUSPENDED: - ret = -ESTRPIPE; - goto __end; - default: - ret = -EBADFD; - goto __end; - } - - avail = snd_pcm_capture_avail(runtime); - if (avail <= 0) { - ret = 0; - goto __end; - } - if (frames > (snd_pcm_uframes_t)avail) - frames = avail; - appl_ptr = runtime->control->appl_ptr + frames; - if (appl_ptr >= (snd_pcm_sframes_t)runtime->boundary) - appl_ptr -= runtime->boundary; - runtime->control->appl_ptr = appl_ptr; - ret = frames; - __end: + ret = do_pcm_hwsync(substream); + if (!ret) + ret = forward_appl_ptr(substream, frames, + snd_pcm_capture_avail(runtime)); snd_pcm_stream_unlock_irq(substream); return ret; } static int snd_pcm_hwsync(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; int err; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_DRAINING: - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - goto __badfd; - /* Fall through */ - case SNDRV_PCM_STATE_RUNNING: - if ((err = snd_pcm_update_hw_ptr(substream)) < 0) - break; - /* Fall through */ - case SNDRV_PCM_STATE_PREPARED: - err = 0; - break; - case SNDRV_PCM_STATE_SUSPENDED: - err = -ESTRPIPE; - break; - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - break; - default: - __badfd: - err = -EBADFD; - break; - } + err = do_pcm_hwsync(substream); snd_pcm_stream_unlock_irq(substream); return err; } -static int snd_pcm_delay(struct snd_pcm_substream *substream, - snd_pcm_sframes_t __user *res) +static snd_pcm_sframes_t snd_pcm_delay(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; int err; snd_pcm_sframes_t n = 0; snd_pcm_stream_lock_irq(substream); - switch (runtime->status->state) { - case SNDRV_PCM_STATE_DRAINING: - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - goto __badfd; - /* Fall through */ - case SNDRV_PCM_STATE_RUNNING: - if ((err = snd_pcm_update_hw_ptr(substream)) < 0) - break; - /* Fall through */ - case SNDRV_PCM_STATE_PREPARED: - case SNDRV_PCM_STATE_SUSPENDED: - err = 0; + err = do_pcm_hwsync(substream); + if (!err) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) n = snd_pcm_playback_hw_avail(runtime); else n = snd_pcm_capture_avail(runtime); n += runtime->delay; - break; - case SNDRV_PCM_STATE_XRUN: - err = -EPIPE; - break; - default: - __badfd: - err = -EBADFD; - break; } snd_pcm_stream_unlock_irq(substream); - if (!err) - if (put_user(n, res)) - err = -EFAULT; - return err; + return err < 0 ? err : n; } static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, @@ -2718,10 +2729,16 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream, return err; } snd_pcm_stream_lock_irq(substream); - if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) - control->appl_ptr = sync_ptr.c.control.appl_ptr; - else + if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_APPL)) { + err = pcm_lib_apply_appl_ptr(substream, + sync_ptr.c.control.appl_ptr); + if (err < 0) { + snd_pcm_stream_unlock_irq(substream); + return err; + } + } else { sync_ptr.c.control.appl_ptr = control->appl_ptr; + } if (!(sync_ptr.flags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) control->avail_min = sync_ptr.c.control.avail_min; else @@ -2749,10 +2766,12 @@ static int snd_pcm_tstamp(struct snd_pcm_substream *substream, int __user *_arg) return 0; } -static int snd_pcm_common_ioctl1(struct file *file, +static int snd_pcm_common_ioctl(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { + struct snd_pcm_file *pcm_file = file->private_data; + switch (cmd) { case SNDRV_PCM_IOCTL_PVERSION: return put_user(SNDRV_PCM_VERSION, (int __user *)arg) ? -EFAULT : 0; @@ -2762,6 +2781,11 @@ static int snd_pcm_common_ioctl1(struct file *file, return 0; case SNDRV_PCM_IOCTL_TTSTAMP: return snd_pcm_tstamp(substream, arg); + case SNDRV_PCM_IOCTL_USER_PVERSION: + if (get_user(pcm_file->user_pversion, + (unsigned int __user *)arg)) + return -EFAULT; + return 0; case SNDRV_PCM_IOCTL_HW_REFINE: return snd_pcm_hw_refine_user(substream, arg); case SNDRV_PCM_IOCTL_HW_PARAMS: @@ -2781,7 +2805,7 @@ static int snd_pcm_common_ioctl1(struct file *file, case SNDRV_PCM_IOCTL_RESET: return snd_pcm_reset(substream); case SNDRV_PCM_IOCTL_START: - return snd_pcm_action_lock_irq(&snd_pcm_action_start, substream, SNDRV_PCM_STATE_RUNNING); + return snd_pcm_start_lock_irq(substream); case SNDRV_PCM_IOCTL_LINK: return snd_pcm_link(substream, (int)(unsigned long) arg); case SNDRV_PCM_IOCTL_UNLINK: @@ -2793,7 +2817,16 @@ static int snd_pcm_common_ioctl1(struct file *file, case SNDRV_PCM_IOCTL_HWSYNC: return snd_pcm_hwsync(substream); case SNDRV_PCM_IOCTL_DELAY: - return snd_pcm_delay(substream, arg); + { + snd_pcm_sframes_t delay = snd_pcm_delay(substream); + snd_pcm_sframes_t __user *res = arg; + + if (delay < 0) + return delay; + if (put_user(delay, res)) + return -EFAULT; + return 0; + } case SNDRV_PCM_IOCTL_SYNC_PTR: return snd_pcm_sync_ptr(substream, arg); #ifdef CONFIG_SND_SUPPORT_OLD_API @@ -2807,23 +2840,34 @@ static int snd_pcm_common_ioctl1(struct file *file, case SNDRV_PCM_IOCTL_DROP: return snd_pcm_drop(substream); case SNDRV_PCM_IOCTL_PAUSE: - { - int res; - snd_pcm_stream_lock_irq(substream); - res = snd_pcm_pause(substream, (int)(unsigned long)arg); - snd_pcm_stream_unlock_irq(substream); - return res; - } + return snd_pcm_action_lock_irq(&snd_pcm_action_pause, + substream, + (int)(unsigned long)arg); } pcm_dbg(substream->pcm, "unknown ioctl = 0x%x\n", cmd); return -ENOTTY; } +static int snd_pcm_common_ioctl1(struct file *file, + struct snd_pcm_substream *substream, + unsigned int cmd, void __user *arg) +{ + struct snd_card *card = substream->pcm->card; + int res; + + snd_power_lock(card); + res = snd_power_wait(card, SNDRV_CTL_POWER_D0); + if (res >= 0) + res = snd_pcm_common_ioctl(file, substream, cmd, arg); + snd_power_unlock(card); + return res; +} + static int snd_pcm_playback_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { - if (snd_BUG_ON(!substream)) + if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_PLAYBACK)) return -EINVAL; @@ -2903,7 +2947,7 @@ static int snd_pcm_capture_ioctl1(struct file *file, struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { - if (snd_BUG_ON(!substream)) + if (PCM_RUNTIME_CHECK(substream)) return -ENXIO; if (snd_BUG_ON(substream->stream != SNDRV_PCM_STREAM_CAPTURE)) return -EINVAL; @@ -3007,30 +3051,55 @@ static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd, (void __user *)arg); } +/** + * snd_pcm_kernel_ioctl - Execute PCM ioctl in the kernel-space + * @substream: PCM substream + * @cmd: IOCTL cmd + * @arg: IOCTL argument + * + * The function is provided primarily for OSS layer and USB gadget drivers, + * and it allows only the limited set of ioctls (hw_params, sw_params, + * prepare, start, drain, drop, forward). + */ int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { - mm_segment_t fs; - int result; + snd_pcm_uframes_t *frames = arg; + snd_pcm_sframes_t result; - fs = snd_enter_user(); - switch (substream->stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - result = snd_pcm_playback_ioctl1(NULL, substream, cmd, - (void __user *)arg); - break; - case SNDRV_PCM_STREAM_CAPTURE: - result = snd_pcm_capture_ioctl1(NULL, substream, cmd, - (void __user *)arg); - break; + switch (cmd) { + case SNDRV_PCM_IOCTL_FORWARD: + { + /* provided only for OSS; capture-only and no value returned */ + if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) + return -EINVAL; + result = snd_pcm_capture_forward(substream, *frames); + return result < 0 ? result : 0; + } + case SNDRV_PCM_IOCTL_HW_PARAMS: + return snd_pcm_hw_params(substream, arg); + case SNDRV_PCM_IOCTL_SW_PARAMS: + return snd_pcm_sw_params(substream, arg); + case SNDRV_PCM_IOCTL_PREPARE: + return snd_pcm_prepare(substream, NULL); + case SNDRV_PCM_IOCTL_START: + return snd_pcm_start_lock_irq(substream); + case SNDRV_PCM_IOCTL_DRAIN: + return snd_pcm_drain(substream, NULL); + case SNDRV_PCM_IOCTL_DROP: + return snd_pcm_drop(substream); + case SNDRV_PCM_IOCTL_DELAY: + { + result = snd_pcm_delay(substream); + if (result < 0) + return result; + *frames = result; + return 0; + } default: - result = -EINVAL; - break; + return -EINVAL; } - snd_leave_user(fs); - return result; } - EXPORT_SYMBOL(snd_pcm_kernel_ioctl); static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, @@ -3314,10 +3383,41 @@ static int snd_pcm_mmap_control(struct snd_pcm_substream *substream, struct file area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; return 0; } + +static bool pcm_status_mmap_allowed(struct snd_pcm_file *pcm_file) +{ + if (pcm_file->no_compat_mmap) + return false; + /* See pcm_control_mmap_allowed() below. + * Since older alsa-lib requires both status and control mmaps to be + * coupled, we have to disable the status mmap for old alsa-lib, too. + */ + if (pcm_file->user_pversion < SNDRV_PROTOCOL_VERSION(2, 0, 14) && + (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR)) + return false; + return true; +} + +static bool pcm_control_mmap_allowed(struct snd_pcm_file *pcm_file) +{ + if (pcm_file->no_compat_mmap) + return false; + /* Disallow the control mmap when SYNC_APPLPTR flag is set; + * it enforces the user-space to fall back to snd_pcm_sync_ptr(), + * thus it effectively assures the manual update of appl_ptr. + */ + if (pcm_file->substream->runtime->hw.info & SNDRV_PCM_INFO_SYNC_APPLPTR) + return false; + return true; +} + #else /* ! coherent mmap */ /* * don't support mmap for status and control records. */ +#define pcm_status_mmap_allowed(pcm_file) false +#define pcm_control_mmap_allowed(pcm_file) false + static int snd_pcm_mmap_status(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area) { @@ -3437,7 +3537,6 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, area->vm_page_prot = pgprot_noncached(area->vm_page_prot); return vm_iomap_memory(area, runtime->dma_addr, runtime->dma_bytes); } - EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); #endif /* SNDRV_PCM_INFO_MMAP */ @@ -3486,7 +3585,6 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, atomic_inc(&substream->mmap_count); return err; } - EXPORT_SYMBOL(snd_pcm_mmap_data); static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) @@ -3503,11 +3601,11 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) offset = area->vm_pgoff << PAGE_SHIFT; switch (offset) { case SNDRV_PCM_MMAP_OFFSET_STATUS: - if (pcm_file->no_compat_mmap) + if (!pcm_status_mmap_allowed(pcm_file)) return -ENXIO; return snd_pcm_mmap_status(substream, file, area); case SNDRV_PCM_MMAP_OFFSET_CONTROL: - if (pcm_file->no_compat_mmap) + if (!pcm_control_mmap_allowed(pcm_file)) return -ENXIO; return snd_pcm_mmap_control(substream, file, area); default: @@ -3603,12 +3701,17 @@ static int snd_pcm_hw_refine_old_user(struct snd_pcm_substream *substream, } snd_pcm_hw_convert_from_old_params(params, oparams); err = snd_pcm_hw_refine(substream, params); - snd_pcm_hw_convert_to_old_params(oparams, params); - if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { - if (!err) - err = -EFAULT; - } + if (err < 0) + goto out_old; + err = fixup_unreferenced_params(substream, params); + if (err < 0) + goto out_old; + + snd_pcm_hw_convert_to_old_params(oparams, params); + if (copy_to_user(_oparams, oparams, sizeof(*oparams))) + err = -EFAULT; +out_old: kfree(oparams); out: kfree(params); @@ -3631,14 +3734,16 @@ static int snd_pcm_hw_params_old_user(struct snd_pcm_substream *substream, err = PTR_ERR(oparams); goto out; } + snd_pcm_hw_convert_from_old_params(params, oparams); err = snd_pcm_hw_params(substream, params); - snd_pcm_hw_convert_to_old_params(oparams, params); - if (copy_to_user(_oparams, oparams, sizeof(*oparams))) { - if (!err) - err = -EFAULT; - } + if (err < 0) + goto out_old; + snd_pcm_hw_convert_to_old_params(oparams, params); + if (copy_to_user(_oparams, oparams, sizeof(*oparams))) + err = -EFAULT; +out_old: kfree(oparams); out: kfree(params); diff --git a/sound/core/pcm_param_trace.h b/sound/core/pcm_param_trace.h new file mode 100644 index 000000000000..86c8d658a25c --- /dev/null +++ b/sound/core/pcm_param_trace.h @@ -0,0 +1,142 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM snd_pcm + +#if !defined(_PCM_PARAMS_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _PCM_PARAMS_TRACE_H + +#include <linux/tracepoint.h> + +#define HW_PARAM_ENTRY(param) {SNDRV_PCM_HW_PARAM_##param, #param} +#define hw_param_labels \ + HW_PARAM_ENTRY(ACCESS), \ + HW_PARAM_ENTRY(FORMAT), \ + HW_PARAM_ENTRY(SUBFORMAT), \ + HW_PARAM_ENTRY(SAMPLE_BITS), \ + HW_PARAM_ENTRY(FRAME_BITS), \ + HW_PARAM_ENTRY(CHANNELS), \ + HW_PARAM_ENTRY(RATE), \ + HW_PARAM_ENTRY(PERIOD_TIME), \ + HW_PARAM_ENTRY(PERIOD_SIZE), \ + HW_PARAM_ENTRY(PERIOD_BYTES), \ + HW_PARAM_ENTRY(PERIODS), \ + HW_PARAM_ENTRY(BUFFER_TIME), \ + HW_PARAM_ENTRY(BUFFER_SIZE), \ + HW_PARAM_ENTRY(BUFFER_BYTES), \ + HW_PARAM_ENTRY(TICK_TIME) + +TRACE_EVENT(hw_mask_param, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_hw_param_t type, int index, const struct snd_mask *prev, const struct snd_mask *curr), + TP_ARGS(substream, type, index, prev, curr), + TP_STRUCT__entry( + __field(int, card) + __field(int, device) + __field(int, subdevice) + __field(int, direction) + __field(snd_pcm_hw_param_t, type) + __field(int, index) + __field(int, total) + __array(__u32, prev_bits, 8) + __array(__u32, curr_bits, 8) + ), + TP_fast_assign( + __entry->card = substream->pcm->card->number; + __entry->device = substream->pcm->device; + __entry->subdevice = substream->number; + __entry->direction = substream->stream; + __entry->type = type; + __entry->index = index; + __entry->total = substream->runtime->hw_constraints.rules_num; + memcpy(__entry->prev_bits, prev->bits, sizeof(__u32) * 8); + memcpy(__entry->curr_bits, curr->bits, sizeof(__u32) * 8); + ), + TP_printk("pcmC%dD%d%s:%d %03d/%03d %s %08x%08x%08x%08x %08x%08x%08x%08x", + __entry->card, + __entry->device, + __entry->direction ? "c" : "p", + __entry->subdevice, + __entry->index, + __entry->total, + __print_symbolic(__entry->type, hw_param_labels), + __entry->prev_bits[3], __entry->prev_bits[2], + __entry->prev_bits[1], __entry->prev_bits[0], + __entry->curr_bits[3], __entry->curr_bits[2], + __entry->curr_bits[1], __entry->curr_bits[0] + ) +); + +TRACE_EVENT(hw_interval_param, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_hw_param_t type, int index, const struct snd_interval *prev, const struct snd_interval *curr), + TP_ARGS(substream, type, index, prev, curr), + TP_STRUCT__entry( + __field(int, card) + __field(int, device) + __field(int, subdevice) + __field(int, direction) + __field(snd_pcm_hw_param_t, type) + __field(int, index) + __field(int, total) + __field(unsigned int, prev_min) + __field(unsigned int, prev_max) + __field(unsigned int, prev_openmin) + __field(unsigned int, prev_openmax) + __field(unsigned int, prev_integer) + __field(unsigned int, prev_empty) + __field(unsigned int, curr_min) + __field(unsigned int, curr_max) + __field(unsigned int, curr_openmin) + __field(unsigned int, curr_openmax) + __field(unsigned int, curr_integer) + __field(unsigned int, curr_empty) + ), + TP_fast_assign( + __entry->card = substream->pcm->card->number; + __entry->device = substream->pcm->device; + __entry->subdevice = substream->number; + __entry->direction = substream->stream; + __entry->type = type; + __entry->index = index; + __entry->total = substream->runtime->hw_constraints.rules_num; + __entry->prev_min = prev->min; + __entry->prev_max = prev->max; + __entry->prev_openmin = prev->openmin; + __entry->prev_openmax = prev->openmax; + __entry->prev_integer = prev->integer; + __entry->prev_empty = prev->empty; + __entry->curr_min = curr->min; + __entry->curr_max = curr->max; + __entry->curr_openmin = curr->openmin; + __entry->curr_openmax = curr->openmax; + __entry->curr_integer = curr->integer; + __entry->curr_empty = curr->empty; + ), + TP_printk("pcmC%dD%d%s:%d %03d/%03d %s %d %d %s%u %u%s %d %d %s%u %u%s", + __entry->card, + __entry->device, + __entry->direction ? "c" : "p", + __entry->subdevice, + __entry->index, + __entry->total, + __print_symbolic(__entry->type, hw_param_labels), + __entry->prev_empty, + __entry->prev_integer, + __entry->prev_openmin ? "(" : "[", + __entry->prev_min, + __entry->prev_max, + __entry->prev_openmax ? ")" : "]", + __entry->curr_empty, + __entry->curr_integer, + __entry->curr_openmin ? "(" : "[", + __entry->curr_min, + __entry->curr_max, + __entry->curr_openmax ? ")" : "]" + ) +); + +#endif /* _PCM_PARAMS_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE pcm_param_trace +#include <trace/define_trace.h> diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c index 20ecd8f18080..11389f13de73 100644 --- a/sound/core/pcm_timer.c +++ b/sound/core/pcm_timer.c @@ -25,6 +25,8 @@ #include <sound/pcm.h> #include <sound/timer.h> +#include "pcm_local.h" + /* * Timer functions */ @@ -33,8 +35,8 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) { unsigned long rate, mult, fsize, l, post; struct snd_pcm_runtime *runtime = substream->runtime; - - mult = 1000000000; + + mult = 1000000000; rate = runtime->rate; if (snd_BUG_ON(!rate)) return; @@ -65,7 +67,7 @@ void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer) { struct snd_pcm_substream *substream; - + substream = timer->private_data; return substream->runtime ? substream->runtime->timer_resolution : 0; } @@ -73,7 +75,7 @@ static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer) static int snd_pcm_timer_start(struct snd_timer * timer) { struct snd_pcm_substream *substream; - + substream = snd_timer_chip(timer); substream->timer_running = 1; return 0; @@ -82,7 +84,7 @@ static int snd_pcm_timer_start(struct snd_timer * timer) static int snd_pcm_timer_stop(struct snd_timer * timer) { struct snd_pcm_substream *substream; - + substream = snd_timer_chip(timer); substream->timer_running = 0; return 0; @@ -112,7 +114,7 @@ void snd_pcm_timer_init(struct snd_pcm_substream *substream) { struct snd_timer_id tid; struct snd_timer *timer; - + tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; tid.dev_class = SNDRV_TIMER_CLASS_PCM; tid.card = substream->pcm->card->number; diff --git a/sound/core/pcm_trace.h b/sound/core/pcm_trace.h index b63b654da5ff..3ddec1b8ae46 100644 --- a/sound/core/pcm_trace.h +++ b/sound/core/pcm_trace.h @@ -34,9 +34,9 @@ TRACE_EVENT(hwptr, __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr; __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base; ), - TP_printk("pcmC%dD%d%c/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu", + TP_printk("pcmC%dD%d%s/sub%d: %s: pos=%lu, old=%lu, base=%lu, period=%lu, buf=%lu", __entry->card, __entry->device, - __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c", __entry->number, __entry->in_interrupt ? "IRQ" : "POS", (unsigned long)__entry->pos, @@ -69,9 +69,9 @@ TRACE_EVENT(xrun, __entry->old_hw_ptr = (substream)->runtime->status->hw_ptr; __entry->hw_ptr_base = (substream)->runtime->hw_ptr_base; ), - TP_printk("pcmC%dD%d%c/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu", + TP_printk("pcmC%dD%d%s/sub%d: XRUN: old=%lu, base=%lu, period=%lu, buf=%lu", __entry->card, __entry->device, - __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c", __entry->number, (unsigned long)__entry->old_hw_ptr, (unsigned long)__entry->hw_ptr_base, @@ -96,12 +96,50 @@ TRACE_EVENT(hw_ptr_error, __entry->stream = (substream)->stream; __entry->reason = (why); ), - TP_printk("pcmC%dD%d%c/sub%d: ERROR: %s", + TP_printk("pcmC%dD%d%s/sub%d: ERROR: %s", __entry->card, __entry->device, - __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c', + __entry->stream == SNDRV_PCM_STREAM_PLAYBACK ? "p" : "c", __entry->number, __entry->reason) ); +TRACE_EVENT(applptr, + TP_PROTO(struct snd_pcm_substream *substream, snd_pcm_uframes_t prev, snd_pcm_uframes_t curr), + TP_ARGS(substream, prev, curr), + TP_STRUCT__entry( + __field( unsigned int, card ) + __field( unsigned int, device ) + __field( unsigned int, number ) + __field( unsigned int, stream ) + __field( snd_pcm_uframes_t, prev ) + __field( snd_pcm_uframes_t, curr ) + __field( snd_pcm_uframes_t, avail ) + __field( snd_pcm_uframes_t, period_size ) + __field( snd_pcm_uframes_t, buffer_size ) + ), + TP_fast_assign( + __entry->card = (substream)->pcm->card->number; + __entry->device = (substream)->pcm->device; + __entry->number = (substream)->number; + __entry->stream = (substream)->stream; + __entry->prev = (prev); + __entry->curr = (curr); + __entry->avail = (substream)->stream ? snd_pcm_capture_avail(substream->runtime) : snd_pcm_playback_avail(substream->runtime); + __entry->period_size = (substream)->runtime->period_size; + __entry->buffer_size = (substream)->runtime->buffer_size; + ), + TP_printk("pcmC%dD%d%s/sub%d: prev=%lu, curr=%lu, avail=%lu, period=%lu, buf=%lu", + __entry->card, + __entry->device, + __entry->stream ? "c" : "p", + __entry->number, + __entry->prev, + __entry->curr, + __entry->avail, + __entry->period_size, + __entry->buffer_size + ) +); + #endif /* _PCM_TRACE_H */ /* This part must be outside protection */ diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 32588ad05653..b3b353d72527 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1610,7 +1610,7 @@ static int snd_rawmidi_dev_free(struct snd_device *device) return snd_rawmidi_free(rmidi); } -#if IS_REACHABLE(CONFIG_SND_SEQUENCER) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) static void snd_rawmidi_dev_seq_free(struct snd_seq_device *device) { struct snd_rawmidi *rmidi = device->private_data; @@ -1691,7 +1691,7 @@ static int snd_rawmidi_dev_register(struct snd_device *device) } } rmidi->proc_entry = entry; -#if IS_REACHABLE(CONFIG_SND_SEQUENCER) +#if IS_ENABLED(CONFIG_SND_SEQUENCER) if (!rmidi->ops || !rmidi->ops->dev_register) { /* own registration mechanism */ if (snd_seq_device_new(rmidi->card, rmidi->device, SNDRV_SEQ_DEV_ID_MIDISYNTH, 0, &rmidi->seq_dev) >= 0) { rmidi->seq_dev->private_data = rmidi; diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig index b851fd890a89..a536760a94c2 100644 --- a/sound/core/seq/Kconfig +++ b/sound/core/seq/Kconfig @@ -1,16 +1,62 @@ -# define SND_XXX_SEQ to min(SND_SEQUENCER,SND_XXX) +config SND_SEQUENCER + tristate "Sequencer support" + select SND_TIMER + select SND_SEQ_DEVICE + help + Say Y or M to enable MIDI sequencer and router support. This + feature allows routing and enqueueing of MIDI events. Events + can be processed at a given time. -config SND_RAWMIDI_SEQ - def_tristate SND_SEQUENCER && SND_RAWMIDI + Many programs require this feature, so you should enable it + unless you know what you're doing. -config SND_OPL3_LIB_SEQ - def_tristate SND_SEQUENCER && SND_OPL3_LIB +if SND_SEQUENCER -config SND_OPL4_LIB_SEQ - def_tristate SND_SEQUENCER && SND_OPL4_LIB +config SND_SEQ_DUMMY + tristate "Sequencer dummy client" + help + Say Y here to enable the dummy sequencer client. This client + is a simple MIDI-through client: all normal input events are + redirected to the output port immediately. -config SND_SBAWE_SEQ - def_tristate SND_SEQUENCER && SND_SBAWE + You don't need this unless you want to connect many MIDI + devices or applications together. -config SND_EMU10K1_SEQ - def_tristate SND_SEQUENCER && SND_EMU10K1 + To compile this driver as a module, choose M here: the module + will be called snd-seq-dummy. + +config SND_SEQUENCER_OSS + tristate "OSS Sequencer API" + depends on SND_OSSEMUL + select SND_SEQ_MIDI_EVENT + help + Say Y here to enable OSS sequencer emulation (both + /dev/sequencer and /dev/music interfaces). + + Many programs still use the OSS API, so say Y. + + To compile this driver as a module, choose M here: the module + will be called snd-seq-oss. + +config SND_SEQ_HRTIMER_DEFAULT + bool "Use HR-timer as default sequencer timer" + depends on SND_HRTIMER + default y + help + Say Y here to use the HR-timer backend as the default sequencer + timer. + +config SND_SEQ_MIDI_EVENT + def_tristate SND_RAWMIDI + +config SND_SEQ_MIDI + tristate + select SND_SEQ_MIDI_EVENT + +config SND_SEQ_MIDI_EMUL + tristate + +config SND_SEQ_VIRMIDI + tristate + +endif # SND_SEQUENCER diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile index b65fa5a1943b..68fd367ac39c 100644 --- a/sound/core/seq/Makefile +++ b/sound/core/seq/Makefile @@ -3,7 +3,6 @@ # Copyright (c) 1999 by Jaroslav Kysela <perex@perex.cz> # -snd-seq-device-objs := seq_device.o snd-seq-objs := seq.o seq_lock.o seq_clientmgr.o seq_memory.o seq_queue.o \ seq_fifo.o seq_prioq.o seq_timer.o \ seq_system.o seq_ports.o @@ -14,17 +13,11 @@ snd-seq-midi-event-objs := seq_midi_event.o snd-seq-dummy-objs := seq_dummy.o snd-seq-virmidi-objs := seq_virmidi.o -obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o -ifeq ($(CONFIG_SND_SEQUENCER_OSS),y) - obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o - obj-$(CONFIG_SND_SEQUENCER) += oss/ -endif -obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o +obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o +obj-$(CONFIG_SND_SEQUENCER_OSS) += oss/ -# Toplevel Module Dependency -obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o -obj-$(CONFIG_SND_RAWMIDI_SEQ) += snd-seq-midi.o snd-seq-midi-event.o -obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o -obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o -obj-$(CONFIG_SND_SBAWE_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o -obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o +obj-$(CONFIG_SND_SEQ_DUMMY) += snd-seq-dummy.o +obj-$(CONFIG_SND_SEQ_MIDI) += snd-seq-midi.o +obj-$(CONFIG_SND_SEQ_MIDI_EMUL) += snd-seq-midi-emul.o +obj-$(CONFIG_SND_SEQ_MIDI_EVENT) += snd-seq-midi-event.o +obj-$(CONFIG_SND_SEQ_VIRMIDI) += snd-seq-virmidi.o diff --git a/sound/core/seq/oss/Makefile b/sound/core/seq/oss/Makefile index b38406b8463c..4ea4e3eea6b7 100644 --- a/sound/core/seq/oss/Makefile +++ b/sound/core/seq/oss/Makefile @@ -7,4 +7,4 @@ snd-seq-oss-objs := seq_oss.o seq_oss_init.o seq_oss_timer.o seq_oss_ioctl.o \ seq_oss_event.o seq_oss_rw.o seq_oss_synth.o \ seq_oss_midi.o seq_oss_readq.o seq_oss_writeq.o -obj-$(CONFIG_SND_SEQUENCER) += snd-seq-oss.o +obj-$(CONFIG_SND_SEQUENCER_OSS) += snd-seq-oss.o diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index f3b1d7f50b81..272c55fe17c8 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1668,7 +1668,6 @@ int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo) return -EPERM; return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo); } - EXPORT_SYMBOL(snd_seq_set_queue_tempo); static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client, @@ -2200,7 +2199,6 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index, /* return client number to caller */ return client->number; } - EXPORT_SYMBOL(snd_seq_create_kernel_client); /* exported to kernel modules */ @@ -2219,7 +2217,6 @@ int snd_seq_delete_kernel_client(int client) kfree(ptr); return 0; } - EXPORT_SYMBOL(snd_seq_delete_kernel_client); /* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue @@ -2269,7 +2266,6 @@ int snd_seq_kernel_client_enqueue(int client, struct snd_seq_event * ev, { return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop); } - EXPORT_SYMBOL(snd_seq_kernel_client_enqueue); /* @@ -2283,7 +2279,6 @@ int snd_seq_kernel_client_enqueue_blocking(int client, struct snd_seq_event * ev { return kernel_client_enqueue(client, ev, file, 1, atomic, hop); } - EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking); /* @@ -2321,7 +2316,6 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev, snd_seq_client_unlock(cptr); return result; } - EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); /** @@ -2354,7 +2348,6 @@ int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg) cmd, _IOC_TYPE(cmd), _IOC_NR(cmd)); return -ENOTTY; } - EXPORT_SYMBOL(snd_seq_kernel_client_ctl); /* exported (for OSS emulator) */ @@ -2372,7 +2365,6 @@ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table return 1; return 0; } - EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); /*---------------------------------------------------------------------------*/ diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c index 12ba83367b1b..0ff7926a5a69 100644 --- a/sound/core/seq/seq_lock.c +++ b/sound/core/seq/seq_lock.c @@ -40,7 +40,6 @@ void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line) schedule_timeout_uninterruptible(1); } } - EXPORT_SYMBOL(snd_use_lock_sync_helper); #endif diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index d6e9aacdc36b..f763682584a8 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -118,7 +118,6 @@ int snd_seq_dump_var_event(const struct snd_seq_event *event, } return 0; } - EXPORT_SYMBOL(snd_seq_dump_var_event); @@ -169,7 +168,6 @@ int snd_seq_expand_var_event(const struct snd_seq_event *event, int count, char &buf); return err < 0 ? err : newlen; } - EXPORT_SYMBOL(snd_seq_expand_var_event); /* diff --git a/sound/core/seq/seq_midi_emul.c b/sound/core/seq/seq_midi_emul.c index 7ba937399ac7..9e2912e3e80f 100644 --- a/sound/core/seq/seq_midi_emul.c +++ b/sound/core/seq/seq_midi_emul.c @@ -236,6 +236,7 @@ snd_midi_process_event(struct snd_midi_op *ops, break; } } +EXPORT_SYMBOL(snd_midi_process_event); /* @@ -409,6 +410,7 @@ snd_midi_channel_set_clear(struct snd_midi_channel_set *chset) chan->drum_channel = 0; } } +EXPORT_SYMBOL(snd_midi_channel_set_clear); /* * Process a rpn message. @@ -701,6 +703,7 @@ struct snd_midi_channel_set *snd_midi_channel_alloc_set(int n) } return chset; } +EXPORT_SYMBOL(snd_midi_channel_alloc_set); /* * Reset the midi controllers on a particular channel to default values. @@ -724,6 +727,7 @@ void snd_midi_channel_free_set(struct snd_midi_channel_set *chset) kfree(chset->channels); kfree(chset); } +EXPORT_SYMBOL(snd_midi_channel_free_set); static int __init alsa_seq_midi_emul_init(void) { @@ -736,8 +740,3 @@ static void __exit alsa_seq_midi_emul_exit(void) module_init(alsa_seq_midi_emul_init) module_exit(alsa_seq_midi_emul_exit) - -EXPORT_SYMBOL(snd_midi_process_event); -EXPORT_SYMBOL(snd_midi_channel_set_clear); -EXPORT_SYMBOL(snd_midi_channel_alloc_set); -EXPORT_SYMBOL(snd_midi_channel_free_set); diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c index 37db7ba492a6..90bbbdbeba03 100644 --- a/sound/core/seq/seq_midi_event.c +++ b/sound/core/seq/seq_midi_event.c @@ -134,6 +134,7 @@ int snd_midi_event_new(int bufsize, struct snd_midi_event **rdev) *rdev = dev; return 0; } +EXPORT_SYMBOL(snd_midi_event_new); void snd_midi_event_free(struct snd_midi_event *dev) { @@ -142,6 +143,7 @@ void snd_midi_event_free(struct snd_midi_event *dev) kfree(dev); } } +EXPORT_SYMBOL(snd_midi_event_free); /* * initialize record @@ -161,6 +163,7 @@ void snd_midi_event_reset_encode(struct snd_midi_event *dev) reset_encode(dev); spin_unlock_irqrestore(&dev->lock, flags); } +EXPORT_SYMBOL(snd_midi_event_reset_encode); void snd_midi_event_reset_decode(struct snd_midi_event *dev) { @@ -170,6 +173,7 @@ void snd_midi_event_reset_decode(struct snd_midi_event *dev) dev->lastcmd = 0xff; spin_unlock_irqrestore(&dev->lock, flags); } +EXPORT_SYMBOL(snd_midi_event_reset_decode); #if 0 void snd_midi_event_init(struct snd_midi_event *dev) @@ -183,6 +187,7 @@ void snd_midi_event_no_status(struct snd_midi_event *dev, int on) { dev->nostat = on ? 1 : 0; } +EXPORT_SYMBOL(snd_midi_event_no_status); /* * resize buffer @@ -232,6 +237,7 @@ long snd_midi_event_encode(struct snd_midi_event *dev, unsigned char *buf, long return result; } +EXPORT_SYMBOL(snd_midi_event_encode); /* * read one byte and encode to sequencer event: @@ -307,6 +313,7 @@ int snd_midi_event_encode_byte(struct snd_midi_event *dev, int c, spin_unlock_irqrestore(&dev->lock, flags); return rc; } +EXPORT_SYMBOL(snd_midi_event_encode_byte); /* encode note event */ static void note_event(struct snd_midi_event *dev, struct snd_seq_event *ev) @@ -408,6 +415,7 @@ long snd_midi_event_decode(struct snd_midi_event *dev, unsigned char *buf, long return qlen; } } +EXPORT_SYMBOL(snd_midi_event_decode); /* decode note event */ @@ -524,19 +532,6 @@ static int extra_decode_xrpn(struct snd_midi_event *dev, unsigned char *buf, return idx; } -/* - * exports - */ - -EXPORT_SYMBOL(snd_midi_event_new); -EXPORT_SYMBOL(snd_midi_event_free); -EXPORT_SYMBOL(snd_midi_event_reset_encode); -EXPORT_SYMBOL(snd_midi_event_reset_decode); -EXPORT_SYMBOL(snd_midi_event_no_status); -EXPORT_SYMBOL(snd_midi_event_encode); -EXPORT_SYMBOL(snd_midi_event_encode_byte); -EXPORT_SYMBOL(snd_midi_event_decode); - static int __init alsa_seq_midi_event_init(void) { return 0; diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index fe686ee41c6d..0a7020c82bfc 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -685,7 +685,6 @@ int snd_seq_event_port_attach(int client, return ret; } - EXPORT_SYMBOL(snd_seq_event_port_attach); /* @@ -706,5 +705,4 @@ int snd_seq_event_port_detach(int client, int port) return err; } - EXPORT_SYMBOL(snd_seq_event_port_detach); diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index 52f31f1498f9..8d93a4021c78 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -534,6 +534,7 @@ int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmi *rrmidi = rmidi; return 0; } +EXPORT_SYMBOL(snd_virmidi_new); /* * ENTRY functions @@ -550,5 +551,3 @@ static void __exit alsa_virmidi_exit(void) module_init(alsa_virmidi_init) module_exit(alsa_virmidi_exit) - -EXPORT_SYMBOL(snd_virmidi_new); diff --git a/sound/core/seq/seq_device.c b/sound/core/seq_device.c index c4acf17e9f5e..c4acf17e9f5e 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq_device.c diff --git a/sound/core/sound.c b/sound/core/sound.c index 175f9e4e01c8..b30f027eb0fe 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -74,7 +74,6 @@ void snd_request_card(int card) return; request_module("snd-card-%i", card); } - EXPORT_SYMBOL(snd_request_card); static void snd_request_other(int minor) @@ -124,7 +123,6 @@ void *snd_lookup_minor_data(unsigned int minor, int type) mutex_unlock(&sound_mutex); return private_data; } - EXPORT_SYMBOL(snd_lookup_minor_data); #ifdef CONFIG_MODULES diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 0ca9d72b2273..0a5c66229a22 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -55,7 +55,6 @@ void *snd_lookup_oss_minor_data(unsigned int minor, int type) mutex_unlock(&sound_oss_mutex); return private_data; } - EXPORT_SYMBOL(snd_lookup_oss_minor_data); static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev) @@ -159,7 +158,6 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev, kfree(preg); return -EBUSY; } - EXPORT_SYMBOL(snd_register_oss_device); int snd_unregister_oss_device(int type, struct snd_card *card, int dev) @@ -200,7 +198,6 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev) kfree(mptr); return 0; } - EXPORT_SYMBOL(snd_unregister_oss_device); /* diff --git a/sound/core/timer.c b/sound/core/timer.c index 884c3066b028..a9b9a277e00c 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -319,6 +319,7 @@ int snd_timer_open(struct snd_timer_instance **ti, *ti = timeri; return 0; } +EXPORT_SYMBOL(snd_timer_open); /* * close a timer instance @@ -384,6 +385,7 @@ int snd_timer_close(struct snd_timer_instance *timeri) mutex_unlock(®ister_mutex); return 0; } +EXPORT_SYMBOL(snd_timer_close); unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) { @@ -398,6 +400,7 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) } return 0; } +EXPORT_SYMBOL(snd_timer_resolution); static void snd_timer_notify1(struct snd_timer_instance *ti, int event) { @@ -589,6 +592,7 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) else return snd_timer_start1(timeri, true, ticks); } +EXPORT_SYMBOL(snd_timer_start); /* * stop the timer instance. @@ -602,6 +606,7 @@ int snd_timer_stop(struct snd_timer_instance *timeri) else return snd_timer_stop1(timeri, true); } +EXPORT_SYMBOL(snd_timer_stop); /* * start again.. the tick is kept. @@ -617,6 +622,7 @@ int snd_timer_continue(struct snd_timer_instance *timeri) else return snd_timer_start1(timeri, false, 0); } +EXPORT_SYMBOL(snd_timer_continue); /* * pause.. remember the ticks left @@ -628,6 +634,7 @@ int snd_timer_pause(struct snd_timer_instance * timeri) else return snd_timer_stop1(timeri, false); } +EXPORT_SYMBOL(snd_timer_pause); /* * reschedule the timer @@ -809,6 +816,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) if (use_tasklet) tasklet_schedule(&timer->task_queue); } +EXPORT_SYMBOL(snd_timer_interrupt); /* @@ -859,6 +867,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, *rtimer = timer; return 0; } +EXPORT_SYMBOL(snd_timer_new); static int snd_timer_free(struct snd_timer *timer) { @@ -978,6 +987,7 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam } spin_unlock_irqrestore(&timer->lock, flags); } +EXPORT_SYMBOL(snd_timer_notify); /* * exported functions for global timers @@ -993,11 +1003,13 @@ int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer) tid.subdevice = 0; return snd_timer_new(NULL, id, &tid, rtimer); } +EXPORT_SYMBOL(snd_timer_global_new); int snd_timer_global_free(struct snd_timer *timer) { return snd_timer_free(timer); } +EXPORT_SYMBOL(snd_timer_global_free); int snd_timer_global_register(struct snd_timer *timer) { @@ -1007,6 +1019,7 @@ int snd_timer_global_register(struct snd_timer *timer) dev.device_data = timer; return snd_timer_dev_register(&dev); } +EXPORT_SYMBOL(snd_timer_global_register); /* * System timer @@ -1327,6 +1340,33 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, wake_up(&tu->qchange_sleep); } +static int realloc_user_queue(struct snd_timer_user *tu, int size) +{ + struct snd_timer_read *queue = NULL; + struct snd_timer_tread *tqueue = NULL; + + if (tu->tread) { + tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL); + if (!tqueue) + return -ENOMEM; + } else { + queue = kcalloc(size, sizeof(*queue), GFP_KERNEL); + if (!queue) + return -ENOMEM; + } + + spin_lock_irq(&tu->qlock); + kfree(tu->queue); + kfree(tu->tqueue); + tu->queue_size = size; + tu->queue = queue; + tu->tqueue = tqueue; + tu->qhead = tu->qtail = tu->qused = 0; + spin_unlock_irq(&tu->qlock); + + return 0; +} + static int snd_timer_user_open(struct inode *inode, struct file *file) { struct snd_timer_user *tu; @@ -1343,10 +1383,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file) init_waitqueue_head(&tu->qchange_sleep); mutex_init(&tu->ioctl_lock); tu->ticks = 1; - tu->queue_size = 128; - tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read), - GFP_KERNEL); - if (tu->queue == NULL) { + if (realloc_user_queue(tu, 128) < 0) { kfree(tu); return -ENOMEM; } @@ -1618,34 +1655,12 @@ static int snd_timer_user_tselect(struct file *file, if (err < 0) goto __err; - tu->qhead = tu->qtail = tu->qused = 0; - kfree(tu->queue); - tu->queue = NULL; - kfree(tu->tqueue); - tu->tqueue = NULL; - if (tu->tread) { - tu->tqueue = kmalloc(tu->queue_size * sizeof(struct snd_timer_tread), - GFP_KERNEL); - if (tu->tqueue == NULL) - err = -ENOMEM; - } else { - tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read), - GFP_KERNEL); - if (tu->queue == NULL) - err = -ENOMEM; - } - - if (err < 0) { - snd_timer_close(tu->timeri); - tu->timeri = NULL; - } else { - tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; - tu->timeri->callback = tu->tread + tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; + tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; - tu->timeri->ccallback = snd_timer_user_ccallback; - tu->timeri->callback_data = (void *)tu; - tu->timeri->disconnect = snd_timer_user_disconnect; - } + tu->timeri->ccallback = snd_timer_user_ccallback; + tu->timeri->callback_data = (void *)tu; + tu->timeri->disconnect = snd_timer_user_disconnect; __err: return err; @@ -1687,8 +1702,6 @@ static int snd_timer_user_params(struct file *file, struct snd_timer_user *tu; struct snd_timer_params params; struct snd_timer *t; - struct snd_timer_read *tr; - struct snd_timer_tread *ttr; int err; tu = file->private_data; @@ -1751,24 +1764,11 @@ static int snd_timer_user_params(struct file *file, spin_unlock_irq(&t->lock); if (params.queue_size > 0 && (unsigned int)tu->queue_size != params.queue_size) { - if (tu->tread) { - ttr = kmalloc(params.queue_size * sizeof(*ttr), - GFP_KERNEL); - if (ttr) { - kfree(tu->tqueue); - tu->queue_size = params.queue_size; - tu->tqueue = ttr; - } - } else { - tr = kmalloc(params.queue_size * sizeof(*tr), - GFP_KERNEL); - if (tr) { - kfree(tu->queue); - tu->queue_size = params.queue_size; - tu->queue = tr; - } - } + err = realloc_user_queue(tu, params.queue_size); + if (err < 0) + goto _end; } + spin_lock_irq(&tu->qlock); tu->qhead = tu->qtail = tu->qused = 0; if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { if (tu->tread) { @@ -1789,6 +1789,7 @@ static int snd_timer_user_params(struct file *file, } tu->filter = params.filter; tu->ticks = params.ticks; + spin_unlock_irq(&tu->qlock); err = 0; _end: if (copy_to_user(_params, ¶ms, sizeof(params))) @@ -1891,13 +1892,19 @@ static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, return snd_timer_user_next_device(argp); case SNDRV_TIMER_IOCTL_TREAD: { - int xarg; + int xarg, old_tread; if (tu->timeri) /* too late */ return -EBUSY; if (get_user(xarg, p)) return -EFAULT; + old_tread = tu->tread; tu->tread = xarg ? 1 : 0; + if (tu->tread != old_tread && + realloc_user_queue(tu, tu->queue_size) < 0) { + tu->tread = old_tread; + return -ENOMEM; + } return 0; } case SNDRV_TIMER_IOCTL_GINFO: @@ -2030,10 +2037,12 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait) poll_wait(file, &tu->qchange_sleep, wait); mask = 0; + spin_lock_irq(&tu->qlock); if (tu->qused) mask |= POLLIN | POLLRDNORM; if (tu->disconnected) mask |= POLLERR; + spin_unlock_irq(&tu->qlock); return mask; } @@ -2117,17 +2126,3 @@ static void __exit alsa_timer_exit(void) module_init(alsa_timer_init) module_exit(alsa_timer_exit) - -EXPORT_SYMBOL(snd_timer_open); -EXPORT_SYMBOL(snd_timer_close); -EXPORT_SYMBOL(snd_timer_resolution); -EXPORT_SYMBOL(snd_timer_start); -EXPORT_SYMBOL(snd_timer_stop); -EXPORT_SYMBOL(snd_timer_continue); -EXPORT_SYMBOL(snd_timer_pause); -EXPORT_SYMBOL(snd_timer_new); -EXPORT_SYMBOL(snd_timer_notify); -EXPORT_SYMBOL(snd_timer_global_new); -EXPORT_SYMBOL(snd_timer_global_free); -EXPORT_SYMBOL(snd_timer_global_register); -EXPORT_SYMBOL(snd_timer_interrupt); |