[alsa-devel] [PATCH v2 00/27] Revised full patchset for PCM in-kernel copy support
Hi,
this is a full patchset of what I sent previously, containing the all changes instead of the snippet. The main purpose of this patchset is to eliminate the remaining usages of set_fs(). They are basically used for in-kernel PCM data transfer, and this patch provides the new API functions and replaces the hackish set_fs() calls with them.
Unlike the first patchset with the unified copy_silence ops, this adds a new copy_kernel ops instead. At the same time, copy/silence are changed to receive the position and size in bytes instead of frames. This allows us to simplify the PCM core code. As a result, a good amount of code could be removed from pcm_lib.c.
The difference from the previous patchset is that this is a full patchset, i.e. all relevant drivers have been covered, and also some small issues have been addressed, in addition, the documentation update is provided, too.
I'm Cc'ing the media and the USB people since it touches solo6x10 and usb-gadget drivers.
The previous ACK was dropped as each patch was rewritten again. Sorry for the doubly patch-review labours.
thanks,
Takashi
===
Takashi Iwai (26): ALSA: pcm: Introduce copy_user, copy_kernel and fill_silence ops ALSA: dummy: Convert to new PCM copy ops ALSA: es1938: Convert to the new PCM copy ops ALSA: nm256: Convert to new PCM copy ops ALSA: korg1212: Convert to the new PCM ops ALSA: rme32: Convert to the new PCM copy ops ALSA: rme96: Convert to the new PCM ops ALSA: rme9652: Convert to the new PCM ops ALSA: hdsp: Convert to the new PCM ops ALSA: gus: Convert to the new PCM ops ALSA: sb: Convert to the new PCM ops ALSA: sh: Convert to the new PCM ops ASoC: blackfin: Convert to the new PCM ops [media] solo6x10: Convert to the new PCM ops ALSA: pcm: Drop the old copy and silence ops ALSA: pcm: Check PCM state by a common helper function ALSA: pcm: Shuffle codes ALSA: pcm: Call directly the common read/write helpers ALSA: pcm: More unification of PCM transfer codes ALSA: pcm: Unify read/write loop ALSA: pcm: Simplify snd_pcm_playback_silence() ALSA: pcm: Direct in-kernel read/write support usb: gadget: u_uac1: Kill set_fs() usage ALSA: pcm: Kill set_fs() in PCM OSS layer ALSA: pcm: Build OSS writev/readv helpers conditionally ALSA: doc: Update copy_user, copy_kernel and fill_silence PCM ops
.../sound/kernel-api/writing-an-alsa-driver.rst | 111 ++-- drivers/media/pci/solo6x10/solo6x10-g723.c | 32 +- drivers/usb/gadget/function/u_uac1.c | 7 +- include/sound/pcm.h | 80 ++- sound/core/oss/io.c | 4 +- sound/core/oss/pcm_oss.c | 81 +-- sound/core/oss/pcm_plugin.h | 6 +- sound/core/pcm_lib.c | 564 ++++++++------------- sound/drivers/dummy.c | 20 +- sound/isa/gus/gus_pcm.c | 97 ++-- sound/isa/sb/emu8000_pcm.c | 190 ++++--- sound/pci/es1938.c | 33 +- sound/pci/korg1212/korg1212.c | 112 ++-- sound/pci/nm256/nm256.c | 57 ++- sound/pci/rme32.c | 65 ++- sound/pci/rme96.c | 70 ++- sound/pci/rme9652/hdsp.c | 67 ++- sound/pci/rme9652/rme9652.c | 71 ++- sound/sh/sh_dac_audio.c | 54 +- sound/soc/blackfin/bf5xx-ac97-pcm.c | 27 +- sound/soc/blackfin/bf5xx-i2s-pcm.c | 36 +- sound/soc/soc-pcm.c | 5 +- 22 files changed, 977 insertions(+), 812 deletions(-)
The previous commit [63691587f7b0: ALSA: hda - Apply dual-codec quirk for MSI Z270-Gaming mobo] attempted to apply the existing dual-codec quirk for a MSI mobo. But it turned out that this isn't applied properly due to the MSI-vendor quirk before this entry. I overlooked such two MSI entries just because they were put in the wrong position, although we have a list ordered by PCI SSID numbers.
This patch fixes it by rearranging the unordered entries.
Fixes: 63691587f7b0 ("ALSA: hda - Apply dual-codec quirk for MSI Z270-Gaming mobo") Reported-by: Rudolf Schmidt info@rudolfschmidt.com Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 918e45268915..a57988d617e9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2324,11 +2324,11 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_MBA11_VREF),
SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD), - SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), - SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), + SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS), + SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
On Thu, 01 Jun 2017 22:58:24 +0200, Takashi Iwai wrote:
The previous commit [63691587f7b0: ALSA: hda - Apply dual-codec quirk for MSI Z270-Gaming mobo] attempted to apply the existing dual-codec quirk for a MSI mobo. But it turned out that this isn't applied properly due to the MSI-vendor quirk before this entry. I overlooked such two MSI entries just because they were put in the wrong position, although we have a list ordered by PCI SSID numbers.
This patch fixes it by rearranging the unordered entries.
Fixes: 63691587f7b0 ("ALSA: hda - Apply dual-codec quirk for MSI Z270-Gaming mobo") Reported-by: Rudolf Schmidt info@rudolfschmidt.com Signed-off-by: Takashi Iwai tiwai@suse.de
Gah, an irrelevant patch I wanted to submit slipped into the patch series. Please disregard one, sorry.
Takashi
sound/pci/hda/patch_realtek.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 918e45268915..a57988d617e9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2324,11 +2324,11 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x106b, 0x4a00, "Macbook 5,2", ALC889_FIXUP_MBA11_VREF),
SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC882_FIXUP_EAPD),
- SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD),
- SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE), SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
- SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD), SND_PCI_QUIRK(0x1462, 0xda57, "MSI Z270-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
- SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3), SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX), SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
-- 2.13.0
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Hello!
On 6/1/2017 11:58 PM, Takashi Iwai wrote:
The previous commit [63691587f7b0: ALSA: hda - Apply dual-codec quirk for MSI Z270-Gaming mobo] attempted to apply the existing dual-codec
The standard way of citing a commit is: commit 63691587f7b0 ("ALSA: hda - Apply dual-codec quirk for MSI Z270-Gaming mobo"), just like in the Fixes: tag.
quirk for a MSI mobo. But it turned out that this isn't applied properly due to the MSI-vendor quirk before this entry. I overlooked such two MSI entries just because they were put in the wrong position, although we have a list ordered by PCI SSID numbers.
This patch fixes it by rearranging the unordered entries.
Fixes: 63691587f7b0 ("ALSA: hda - Apply dual-codec quirk for MSI Z270-Gaming mobo") Reported-by: Rudolf Schmidt info@rudolfschmidt.com Signed-off-by: Takashi Iwai tiwai@suse.de
[...]
MBR, Sergei
For supporting the explicit in-kernel copy of PCM buffer data, and also for further code refactoring, three new PCM ops, copy_user, copy_kernel and fill_silence, are introduced. The old copy and silence ops will be deprecated and removed later once when all callers are converted.
The copy_kernel ops is the new one, and it's supposed to transfer the PCM data from the given kernel buffer to the hardware ring-buffer (or vice-versa depending on the stream direction), while the copy_user ops is equivalent with the former copy ops, to transfer the data from the user-space buffer.
The major difference of the new copy_* and fill_silence ops from the previous ops is that the new ops take bytes instead of frames for size and position arguments. It has two merits: first, it allows the callback implementation often simpler (just call directly memcpy() & co), and second, it may unify the implementations of both interleaved and non-interleaved cases, as we'll see in the later patch.
As of this stage, copy_kernel ops isn't referred yet, but only copy_user is used.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/pcm.h | 7 +++++ sound/core/pcm_lib.c | 89 +++++++++++++++++++++++++++++++++++++++++++--------- sound/soc/soc-pcm.c | 3 ++ 3 files changed, 84 insertions(+), 15 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index c609b891c4c2..a065415191d8 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -83,6 +83,13 @@ struct snd_pcm_ops { void __user *buf, snd_pcm_uframes_t count); int (*silence)(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count); + int (*fill_silence)(struct snd_pcm_substream *substream, int channel, + unsigned long pos, unsigned long bytes); + int (*copy_user)(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void __user *buf, + unsigned long bytes); + int (*copy_kernel)(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void *buf, unsigned long bytes); struct page *(*page)(struct snd_pcm_substream *substream, unsigned long offset); int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ab4b1d1e44ee..9334fc2c20c8 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -55,6 +55,8 @@ 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; + char *hwbuf; + int err;
if (runtime->silence_size < runtime->boundary) { snd_pcm_sframes_t noise_dist, n; @@ -109,27 +111,37 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram 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; + if (substream->ops->fill_silence) { + err = substream->ops->fill_silence(substream, 0, + frames_to_bytes(runtime, ofs), + frames_to_bytes(runtime, transfer)); + snd_BUG_ON(err < 0); + } else if (substream->ops->silence) { err = substream->ops->silence(substream, -1, ofs, transfer); snd_BUG_ON(err < 0); } else { - char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); + 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) { + if (substream->ops->fill_silence) { + for (c = 0; c < channels; ++c) { + err = substream->ops->fill_silence(substream, c, + samples_to_bytes(runtime, ofs), + samples_to_bytes(runtime, transfer)); + snd_BUG_ON(err < 0); + } + } else 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); + hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, ofs); snd_pcm_format_set_silence(runtime->format, hwbuf, transfer); } } @@ -1995,7 +2007,13 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, 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 (substream->ops->copy_user) { + hwoff = frames_to_bytes(runtime, hwoff); + frames = frames_to_bytes(runtime, frames); + err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); + if (err < 0) + return err; + } else if (substream->ops->copy) { if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) return err; } else { @@ -2119,7 +2137,8 @@ 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 && !substream->ops->copy + && !runtime->dma_area)) return -EINVAL; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; @@ -2156,8 +2175,30 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, int err; void __user **bufs = (void __user **)data; int channels = runtime->channels; + char __user *buf; int c; - if (substream->ops->copy) { + + if (substream->ops->copy_user) { + hwoff = samples_to_bytes(runtime, hwoff); + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + for (c = 0; c < channels; ++c, ++bufs) { + buf = *bufs + off; + if (!*bufs) { + if (snd_BUG_ON(!substream->ops->fill_silence)) + return -EINVAL; + err = substream->ops->fill_silence(substream, c, + hwoff, + frames); + } else { + err = substream->ops->copy_user(substream, c, + hwoff, buf, + frames); + } + if (err < 0) + return err; + } + } else if (substream->ops->copy) { if (snd_BUG_ON(!substream->ops->silence)) return -EINVAL; for (c = 0; c < channels; ++c, ++bufs) { @@ -2165,7 +2206,7 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0) return err; } else { - char __user *buf = *bufs + samples_to_bytes(runtime, off); + buf = *bufs + samples_to_bytes(runtime, off); if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0) return err; } @@ -2217,7 +2258,13 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, 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 (substream->ops->copy_user) { + hwoff = frames_to_bytes(runtime, hwoff); + frames = frames_to_bytes(runtime, frames); + err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); + if (err < 0) + return err; + } else if (substream->ops->copy) { if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0) return err; } else { @@ -2365,10 +2412,24 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, int err; void __user **bufs = (void __user **)data; int channels = runtime->channels; + char __user *buf; + char *hwbuf; int c; - if (substream->ops->copy) { + + if (substream->ops->copy_user) { + hwoff = samples_to_bytes(runtime, hwoff); + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + for (c = 0; c < channels; ++c, ++bufs) { + if (!*bufs) + continue; + err = substream->ops->copy_user(substream, c, hwoff, + *bufs + off, frames); + if (err < 0) + return err; + } + } else 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); @@ -2378,8 +2439,6 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, } 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;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index efc5831f205d..8867ed9e5f56 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2743,6 +2743,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
if (platform->driver->ops) { rtd->ops.ack = platform->driver->ops->ack; + rtd->ops.copy_user = platform->driver->ops->copy_user; + rtd->ops.copy_kernel = platform->driver->ops->copy_kernel; + rtd->ops.fill_silence = platform->driver->ops->fill_silence; rtd->ops.copy = platform->driver->ops->copy; rtd->ops.silence = platform->driver->ops->silence; rtd->ops.page = platform->driver->ops->page;
On Thu, Jun 01, 2017 at 10:58:25PM +0200, Takashi Iwai wrote:
For supporting the explicit in-kernel copy of PCM buffer data, and also for further code refactoring, three new PCM ops, copy_user, copy_kernel and fill_silence, are introduced. The old copy and silence ops will be deprecated and removed later once when all callers are converted.
Acked-by: Mark Brown broonie@kernel.org
It's a dummy ops, so just replacing it.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/drivers/dummy.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 172dacd925f5..dd5ed037adf2 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -644,15 +644,22 @@ static int alloc_fake_buffer(void) }
static int dummy_pcm_copy(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - void __user *dst, snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *dst, unsigned long bytes) +{ + return 0; /* do nothing */ +} + +static int dummy_pcm_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long bytes) { return 0; /* do nothing */ }
static int dummy_pcm_silence(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + unsigned long bytes) { return 0; /* do nothing */ } @@ -683,8 +690,9 @@ static struct snd_pcm_ops dummy_pcm_ops_no_buf = { .prepare = dummy_pcm_prepare, .trigger = dummy_pcm_trigger, .pointer = dummy_pcm_pointer, - .copy = dummy_pcm_copy, - .silence = dummy_pcm_silence, + .copy_user = dummy_pcm_copy, + .copy_kernel = dummy_pcm_copy_kernel, + .fill_silence = dummy_pcm_silence, .page = dummy_pcm_page, };
Replace the copy ops with the new copy_user and copy_kernel ops. It's used only for a capture stream (for some hardware workaround), thus we need no silence operation.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/es1938.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-)
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index e8d943071a8c..a544cd52f73a 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -839,15 +839,12 @@ static snd_pcm_uframes_t snd_es1938_playback_pointer(struct snd_pcm_substream *s }
static int snd_es1938_capture_copy(struct snd_pcm_substream *substream, - int channel, - snd_pcm_uframes_t pos, - void __user *dst, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *dst, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct es1938 *chip = snd_pcm_substream_chip(substream); - pos <<= chip->dma1_shift; - count <<= chip->dma1_shift; + if (snd_BUG_ON(pos + count > chip->dma1_size)) return -EINVAL; if (pos + count < chip->dma1_size) { @@ -856,12 +853,31 @@ static int snd_es1938_capture_copy(struct snd_pcm_substream *substream, } else { if (copy_to_user(dst, runtime->dma_area + pos + 1, count - 1)) return -EFAULT; - if (put_user(runtime->dma_area[0], ((unsigned char __user *)dst) + count - 1)) + if (put_user(runtime->dma_area[0], + ((unsigned char __user *)dst) + count - 1)) return -EFAULT; } return 0; }
+static int snd_es1938_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct es1938 *chip = snd_pcm_substream_chip(substream); + + if (snd_BUG_ON(pos + count > chip->dma1_size)) + return -EINVAL; + if (pos + count < chip->dma1_size) { + memcpy(dst, runtime->dma_area + pos + 1, count); + } else { + memcpy(dst, runtime->dma_area + pos + 1, count - 1); + runtime->dma_area[0] = *((unsigned char *)dst + count - 1); + } + return 0; +} + /* * buffer management */ @@ -1012,7 +1028,8 @@ static const struct snd_pcm_ops snd_es1938_capture_ops = { .prepare = snd_es1938_capture_prepare, .trigger = snd_es1938_capture_trigger, .pointer = snd_es1938_capture_pointer, - .copy = snd_es1938_capture_copy, + .copy_user = snd_es1938_capture_copy, + .copy_kernel = snd_es1938_capture_copy_kernel, };
static int snd_es1938_new_pcm(struct es1938 *chip, int device)
Replace the copy and the silence ops with the new ops. The conversion is straightforward with standard helper functions, and now we can drop the bytes <-> frames conversions in callbacks.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/nm256/nm256.c | 57 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 20 deletions(-)
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 103fe311e5a9..63f0985dae27 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -695,53 +695,68 @@ snd_nm256_capture_pointer(struct snd_pcm_substream *substream) */ static int snd_nm256_playback_silence(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int channel, unsigned long pos, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - count = frames_to_bytes(runtime, count); - pos = frames_to_bytes(runtime, pos); + memset_io(s->bufptr + pos, 0, count); return 0; }
static int snd_nm256_playback_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *src, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - count = frames_to_bytes(runtime, count); - pos = frames_to_bytes(runtime, pos); + if (copy_from_user_toio(s->bufptr + pos, src, count)) return -EFAULT; return 0; }
+static int +snd_nm256_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct nm256_stream *s = runtime->private_data; + + memcpy_toio(s->bufptr + pos, src, count); + return 0; +} + /* * copy to user */ static int snd_nm256_capture_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *dst, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *dst, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct nm256_stream *s = runtime->private_data; - count = frames_to_bytes(runtime, count); - pos = frames_to_bytes(runtime, pos); + if (copy_to_user_fromio(dst, s->bufptr + pos, count)) return -EFAULT; return 0; }
+static int +snd_nm256_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct nm256_stream *s = runtime->private_data; + + memcpy_fromio(dst, s->bufptr + pos, count); + return 0; +} + #endif /* !__i386__ */
@@ -911,8 +926,9 @@ static const struct snd_pcm_ops snd_nm256_playback_ops = { .trigger = snd_nm256_playback_trigger, .pointer = snd_nm256_playback_pointer, #ifndef __i386__ - .copy = snd_nm256_playback_copy, - .silence = snd_nm256_playback_silence, + .copy_user = snd_nm256_playback_copy, + .copy_kernel = snd_nm256_playback_copy_kernel, + .fill_silence = snd_nm256_playback_silence, #endif .mmap = snd_pcm_lib_mmap_iomem, }; @@ -926,7 +942,8 @@ static const struct snd_pcm_ops snd_nm256_capture_ops = { .trigger = snd_nm256_capture_trigger, .pointer = snd_nm256_capture_pointer, #ifndef __i386__ - .copy = snd_nm256_capture_copy, + .copy_user = snd_nm256_capture_copy, + .copy_kernel = snd_nm256_capture_copy_kernel, #endif .mmap = snd_pcm_lib_mmap_iomem, };
Replace the copy and the silence ops with the new PCM ops. Although we can refactor this messy code, at this time, the changes are kept as small as possible. Let's clean up later.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/korg1212/korg1212.c | 112 ++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 47 deletions(-)
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index 1e25095fd144..b28fe4914d6b 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -1299,13 +1299,21 @@ static int snd_korg1212_silence(struct snd_korg1212 *korg1212, int pos, int coun return 0; }
-static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst, int pos, int count, int offset, int size) +static int snd_korg1212_copy_to(struct snd_pcm_substream *substream, + void __user *dst, int pos, int count, + bool in_kernel) { - struct KorgAudioFrame * src = korg1212->recordDataBufsPtr[0].bufferData + pos; - int i, rc; - - K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d offset=%d size=%d\n", - pos, offset, size); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); + struct KorgAudioFrame *src; + int i, size; + + pos = bytes_to_frames(runtime, pos); + count = bytes_to_frames(runtime, count); + size = korg1212->channels * 2; + src = korg1212->recordDataBufsPtr[0].bufferData + pos; + K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_to pos=%d size=%d count=%d\n", + pos, size, count); if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES)) return -EINVAL;
@@ -1317,11 +1325,10 @@ static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst, return -EFAULT; } #endif - rc = copy_to_user(dst + offset, src, size); - if (rc) { - K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_to USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i); + if (in_kernel) + memcpy((void *)dst, src, size); + else if (copy_to_user(dst, src, size)) return -EFAULT; - } src++; dst += size; } @@ -1329,13 +1336,22 @@ static int snd_korg1212_copy_to(struct snd_korg1212 *korg1212, void __user *dst, return 0; }
-static int snd_korg1212_copy_from(struct snd_korg1212 *korg1212, void __user *src, int pos, int count, int offset, int size) +static int snd_korg1212_copy_from(struct snd_pcm_substream *substream, + void __user *src, int pos, int count, + bool in_kernel) { - struct KorgAudioFrame * dst = korg1212->playDataBufsPtr[0].bufferData + pos; - int i, rc; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); + struct KorgAudioFrame *dst; + int i, size;
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d offset=%d size=%d count=%d\n", - pos, offset, size, count); + pos = bytes_to_frames(runtime, pos); + count = bytes_to_frames(runtime, count); + size = korg1212->channels * 2; + dst = korg1212->playDataBufsPtr[0].bufferData + pos; + + K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_copy_from pos=%d size=%d count=%d\n", + pos, size, count);
if (snd_BUG_ON(pos + count > K1212_MAX_SAMPLES)) return -EINVAL; @@ -1348,11 +1364,10 @@ static int snd_korg1212_copy_from(struct snd_korg1212 *korg1212, void __user *sr return -EFAULT; } #endif - rc = copy_from_user((void*) dst + offset, src, size); - if (rc) { - K1212_DEBUG_PRINTK("K1212_DEBUG: snd_korg1212_copy_from USER EFAULT src=%p dst=%p iter=%d\n", src, dst, i); + if (in_kernel) + memcpy((void *)dst, src, size); + else if (copy_from_user(dst, src, size)) return -EFAULT; - } dst++; src += size; } @@ -1640,45 +1655,46 @@ static snd_pcm_uframes_t snd_korg1212_capture_pointer(struct snd_pcm_substream * }
static int snd_korg1212_playback_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *src, unsigned long count) { - struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); - - K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_playback_copy [%s] %ld %ld\n", - stateName[korg1212->cardState], pos, count); - - return snd_korg1212_copy_from(korg1212, src, pos, count, 0, korg1212->channels * 2); + return snd_korg1212_copy_from(substream, src, pos, count, false); +}
+static int snd_korg1212_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) +{ + return snd_korg1212_copy_from(substream, (void __user *)src, + pos, count, true); }
static int snd_korg1212_playback_silence(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + unsigned long pos, + unsigned long count) { + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream);
- K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_playback_silence [%s]\n", - stateName[korg1212->cardState]); - - return snd_korg1212_silence(korg1212, pos, count, 0, korg1212->channels * 2); + return snd_korg1212_silence(korg1212, bytes_to_frames(runtime, pos), + bytes_to_frames(runtime, count), + 0, korg1212->channels * 2); }
static int snd_korg1212_capture_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *dst, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *dst, unsigned long count) { - struct snd_korg1212 *korg1212 = snd_pcm_substream_chip(substream); - - K1212_DEBUG_PRINTK_VERBOSE("K1212_DEBUG: snd_korg1212_capture_copy [%s] %ld %ld\n", - stateName[korg1212->cardState], pos, count); + return snd_korg1212_copy_to(substream, dst, pos, count, false); +}
- return snd_korg1212_copy_to(korg1212, dst, pos, count, 0, korg1212->channels * 2); +static int snd_korg1212_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + return snd_korg1212_copy_to(substream, (void __user *)dst, + pos, count, true); }
static const struct snd_pcm_ops snd_korg1212_playback_ops = { @@ -1689,8 +1705,9 @@ static const struct snd_pcm_ops snd_korg1212_playback_ops = { .prepare = snd_korg1212_prepare, .trigger = snd_korg1212_trigger, .pointer = snd_korg1212_playback_pointer, - .copy = snd_korg1212_playback_copy, - .silence = snd_korg1212_playback_silence, + .copy_user = snd_korg1212_playback_copy, + .copy_kernel = snd_korg1212_playback_copy_kernel, + .fill_silence = snd_korg1212_playback_silence, };
static const struct snd_pcm_ops snd_korg1212_capture_ops = { @@ -1701,7 +1718,8 @@ static const struct snd_pcm_ops snd_korg1212_capture_ops = { .prepare = snd_korg1212_prepare, .trigger = snd_korg1212_trigger, .pointer = snd_korg1212_capture_pointer, - .copy = snd_korg1212_capture_copy, + .copy_user = snd_korg1212_capture_copy, + .copy_kernel = snd_korg1212_capture_copy_kernel, };
/*
Replace the copy and the silence ops with the new ops. The conversion is straightforward with standard helper functions, and now we can drop the bytes <-> frames conversions in callbacks.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/rme32.c | 65 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 22 deletions(-)
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 96d15db65dfd..ce438c62b0b3 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -254,39 +254,46 @@ static inline unsigned int snd_rme32_pcm_byteptr(struct rme32 * rme32) }
/* silence callback for halfduplex mode */ -static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) +static int snd_rme32_playback_silence(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + unsigned long count) { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - count <<= rme32->playback_frlog; - pos <<= rme32->playback_frlog; + memset_io(rme32->iobase + RME32_IO_DATA_BUFFER + pos, 0, count); return 0; }
/* copy callback for halfduplex mode */ -static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *src, snd_pcm_uframes_t count) +static int snd_rme32_playback_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *src, unsigned long count) { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - count <<= rme32->playback_frlog; - pos <<= rme32->playback_frlog; + if (copy_from_user_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, - src, count)) + src, count)) return -EFAULT; return 0; }
+static int snd_rme32_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) +{ + struct rme32 *rme32 = snd_pcm_substream_chip(substream); + + memcpy_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos, src, count); + return 0; +} + /* copy callback for halfduplex mode */ -static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *dst, snd_pcm_uframes_t count) +static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *dst, unsigned long count) { struct rme32 *rme32 = snd_pcm_substream_chip(substream); - count <<= rme32->capture_frlog; - pos <<= rme32->capture_frlog; + if (copy_to_user_fromio(dst, rme32->iobase + RME32_IO_DATA_BUFFER + pos, count)) @@ -294,6 +301,16 @@ static int snd_rme32_capture_copy(struct snd_pcm_substream *substream, int chann return 0; }
+static int snd_rme32_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct rme32 *rme32 = snd_pcm_substream_chip(substream); + + memcpy_fromio(dst, rme32->iobase + RME32_IO_DATA_BUFFER + pos, count); + return 0; +} + /* * SPDIF I/O capabilities (half-duplex mode) */ @@ -1205,8 +1222,9 @@ static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = { .prepare = snd_rme32_playback_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_playback_pointer, - .copy = snd_rme32_playback_copy, - .silence = snd_rme32_playback_silence, + .copy_user = snd_rme32_playback_copy, + .copy_kernel = snd_rme32_playback_copy_kernel, + .fill_silence = snd_rme32_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, };
@@ -1219,7 +1237,8 @@ static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = { .prepare = snd_rme32_capture_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_capture_pointer, - .copy = snd_rme32_capture_copy, + .copy_user = snd_rme32_capture_copy, + .copy_kernel = snd_rme32_capture_copy_kernel, .mmap = snd_pcm_lib_mmap_iomem, };
@@ -1231,8 +1250,9 @@ static const struct snd_pcm_ops snd_rme32_playback_adat_ops = { .prepare = snd_rme32_playback_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_playback_pointer, - .copy = snd_rme32_playback_copy, - .silence = snd_rme32_playback_silence, + .copy_user = snd_rme32_playback_copy, + .copy_kernel = snd_rme32_playback_copy_kernel, + .fill_silence = snd_rme32_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, };
@@ -1244,7 +1264,8 @@ static const struct snd_pcm_ops snd_rme32_capture_adat_ops = { .prepare = snd_rme32_capture_prepare, .trigger = snd_rme32_pcm_trigger, .pointer = snd_rme32_capture_pointer, - .copy = snd_rme32_capture_copy, + .copy_user = snd_rme32_capture_copy, + .copy_kernel = snd_rme32_capture_copy_kernel, .mmap = snd_pcm_lib_mmap_iomem, };
Replace the copy and the silence ops with the new PCM ops. The conversion is straightforward with standard helper functions, and now we can drop the bytes <-> frames conversions in callbacks.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/rme96.c | 70 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 26 deletions(-)
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 05b9da30990d..24f1349a8e1b 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -327,13 +327,10 @@ snd_rme96_capture_ptr(struct rme96 *rme96)
static int snd_rme96_playback_silence(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int channel, unsigned long pos, unsigned long count) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - count <<= rme96->playback_frlog; - pos <<= rme96->playback_frlog; + memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, 0, count); return 0; @@ -341,32 +338,49 @@ snd_rme96_playback_silence(struct snd_pcm_substream *substream,
static int snd_rme96_playback_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *src, unsigned long count) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - count <<= rme96->playback_frlog; - pos <<= rme96->playback_frlog; - return copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, - count); + + return copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, + src, count); +} + +static int +snd_rme96_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) +{ + struct rme96 *rme96 = snd_pcm_substream_chip(substream); + + memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src, count); + return 0; }
static int snd_rme96_capture_copy(struct snd_pcm_substream *substream, - int channel, /* not used (interleaved data) */ - snd_pcm_uframes_t pos, - void __user *dst, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + void __user *dst, unsigned long count) { struct rme96 *rme96 = snd_pcm_substream_chip(substream); - count <<= rme96->capture_frlog; - pos <<= rme96->capture_frlog; - return copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, + + return copy_to_user_fromio(dst, + rme96->iobase + RME96_IO_REC_BUFFER + pos, count); }
+static int +snd_rme96_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct rme96 *rme96 = snd_pcm_substream_chip(substream); + + memcpy_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos, count); + return 0; +} + /* * Digital output capabilities (S/PDIF) */ @@ -1513,8 +1527,9 @@ static const struct snd_pcm_ops snd_rme96_playback_spdif_ops = { .prepare = snd_rme96_playback_prepare, .trigger = snd_rme96_playback_trigger, .pointer = snd_rme96_playback_pointer, - .copy = snd_rme96_playback_copy, - .silence = snd_rme96_playback_silence, + .copy_user = snd_rme96_playback_copy, + .copy_kernel = snd_rme96_playback_copy_kernel, + .fill_silence = snd_rme96_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, };
@@ -1526,7 +1541,8 @@ static const struct snd_pcm_ops snd_rme96_capture_spdif_ops = { .prepare = snd_rme96_capture_prepare, .trigger = snd_rme96_capture_trigger, .pointer = snd_rme96_capture_pointer, - .copy = snd_rme96_capture_copy, + .copy_user = snd_rme96_capture_copy, + .copy_kernel = snd_rme96_capture_copy_kernel, .mmap = snd_pcm_lib_mmap_iomem, };
@@ -1538,8 +1554,9 @@ static const struct snd_pcm_ops snd_rme96_playback_adat_ops = { .prepare = snd_rme96_playback_prepare, .trigger = snd_rme96_playback_trigger, .pointer = snd_rme96_playback_pointer, - .copy = snd_rme96_playback_copy, - .silence = snd_rme96_playback_silence, + .copy_user = snd_rme96_playback_copy, + .copy_kernel = snd_rme96_playback_copy_kernel, + .fill_silence = snd_rme96_playback_silence, .mmap = snd_pcm_lib_mmap_iomem, };
@@ -1551,7 +1568,8 @@ static const struct snd_pcm_ops snd_rme96_capture_adat_ops = { .prepare = snd_rme96_capture_prepare, .trigger = snd_rme96_capture_trigger, .pointer = snd_rme96_capture_pointer, - .copy = snd_rme96_capture_copy, + .copy_user = snd_rme96_capture_copy, + .copy_kernel = snd_rme96_capture_copy_kernel, .mmap = snd_pcm_lib_mmap_iomem, };
Replace the copy and the silence ops with the new PCM ops. The conversion is straightforward with standard helper functions.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/rme9652/rme9652.c | 71 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 17 deletions(-)
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 55172c689991..59684bf5cac0 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -1883,13 +1883,14 @@ static char *rme9652_channel_buffer_location(struct snd_rme9652 *rme9652, } }
-static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) +static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *src, unsigned long count) { struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); char *channel_buf;
- if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4)) + if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES)) return -EINVAL;
channel_buf = rme9652_channel_buffer_location (rme9652, @@ -1897,18 +1898,35 @@ static int snd_rme9652_playback_copy(struct snd_pcm_substream *substream, int ch channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_from_user(channel_buf + pos * 4, src, count * 4)) + if (copy_from_user(channel_buf + pos, src, count)) return -EFAULT; - return count; + return 0; }
-static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count) +static int snd_rme9652_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) { struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); char *channel_buf;
- if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES / 4)) + channel_buf = rme9652_channel_buffer_location(rme9652, + substream->pstr->stream, + channel); + if (snd_BUG_ON(!channel_buf)) + return -EIO; + memcpy(channel_buf + pos, src, count); + return 0; +} + +static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *dst, unsigned long count) +{ + struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); + char *channel_buf; + + if (snd_BUG_ON(pos + count > RME9652_CHANNEL_BUFFER_BYTES)) return -EINVAL;
channel_buf = rme9652_channel_buffer_location (rme9652, @@ -1916,13 +1934,30 @@ static int snd_rme9652_capture_copy(struct snd_pcm_substream *substream, int cha channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_to_user(dst, channel_buf + pos * 4, count * 4)) + if (copy_to_user(dst, channel_buf + pos, count)) return -EFAULT; - return count; + return 0; }
-static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +static int snd_rme9652_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); + char *channel_buf; + + channel_buf = rme9652_channel_buffer_location(rme9652, + substream->pstr->stream, + channel); + if (snd_BUG_ON(!channel_buf)) + return -EIO; + memcpy(dst, channel_buf + pos, count); + return 0; +} + +static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + unsigned long count) { struct snd_rme9652 *rme9652 = snd_pcm_substream_chip(substream); char *channel_buf; @@ -1932,8 +1967,8 @@ static int snd_rme9652_hw_silence(struct snd_pcm_substream *substream, int chann channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - memset(channel_buf + pos * 4, 0, count * 4); - return count; + memset(channel_buf + pos, 0, count); + return 0; }
static int snd_rme9652_reset(struct snd_pcm_substream *substream) @@ -2376,8 +2411,9 @@ static const struct snd_pcm_ops snd_rme9652_playback_ops = { .prepare = snd_rme9652_prepare, .trigger = snd_rme9652_trigger, .pointer = snd_rme9652_hw_pointer, - .copy = snd_rme9652_playback_copy, - .silence = snd_rme9652_hw_silence, + .copy_user = snd_rme9652_playback_copy, + .copy_kernel = snd_rme9652_playback_copy_kernel, + .fill_silence = snd_rme9652_hw_silence, };
static const struct snd_pcm_ops snd_rme9652_capture_ops = { @@ -2388,7 +2424,8 @@ static const struct snd_pcm_ops snd_rme9652_capture_ops = { .prepare = snd_rme9652_prepare, .trigger = snd_rme9652_trigger, .pointer = snd_rme9652_hw_pointer, - .copy = snd_rme9652_capture_copy, + .copy_user = snd_rme9652_capture_copy, + .copy_kernel = snd_rme9652_capture_copy_kernel, };
static int snd_rme9652_create_pcm(struct snd_card *card,
Replace the copy and the silence ops with the new PCM ops. The conversion is straightforward with standard helper functions.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/rme9652/hdsp.c | 67 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 17 deletions(-)
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index fc0face6cdc6..b00009644e0e 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -3913,42 +3913,73 @@ static char *hdsp_channel_buffer_location(struct hdsp *hdsp, return hdsp->playback_buffer + (mapped_channel * HDSP_CHANNEL_BUFFER_BYTES); }
-static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) +static int snd_hdsp_playback_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *src, unsigned long count) { struct hdsp *hdsp = snd_pcm_substream_chip(substream); char *channel_buf;
- if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4)) + if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES)) return -EINVAL;
channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_from_user(channel_buf + pos * 4, src, count * 4)) + if (copy_from_user(channel_buf + pos, src, count)) return -EFAULT; - return count; + return 0; }
-static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *dst, snd_pcm_uframes_t count) +static int snd_hdsp_playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) { struct hdsp *hdsp = snd_pcm_substream_chip(substream); char *channel_buf;
- if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES / 4)) + channel_buf = hdsp_channel_buffer_location(hdsp, substream->pstr->stream, channel); + if (snd_BUG_ON(!channel_buf)) + return -EIO; + memcpy(channel_buf + pos, src, count); + return 0; +} + +static int snd_hdsp_capture_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *dst, unsigned long count) +{ + struct hdsp *hdsp = snd_pcm_substream_chip(substream); + char *channel_buf; + + if (snd_BUG_ON(pos + count > HDSP_CHANNEL_BUFFER_BYTES)) return -EINVAL;
channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - if (copy_to_user(dst, channel_buf + pos * 4, count * 4)) + if (copy_to_user(dst, channel_buf + pos, count)) return -EFAULT; - return count; + return 0; }
-static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, snd_pcm_uframes_t count) +static int snd_hdsp_capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count) +{ + struct hdsp *hdsp = snd_pcm_substream_chip(substream); + char *channel_buf; + + channel_buf = hdsp_channel_buffer_location(hdsp, substream->pstr->stream, channel); + if (snd_BUG_ON(!channel_buf)) + return -EIO; + memcpy(dst, channel_buf + pos, count); + return 0; +} + +static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + unsigned long count) { struct hdsp *hdsp = snd_pcm_substream_chip(substream); char *channel_buf; @@ -3956,8 +3987,8 @@ static int snd_hdsp_hw_silence(struct snd_pcm_substream *substream, int channel, channel_buf = hdsp_channel_buffer_location (hdsp, substream->pstr->stream, channel); if (snd_BUG_ON(!channel_buf)) return -EIO; - memset(channel_buf + pos * 4, 0, count * 4); - return count; + memset(channel_buf + pos, 0, count); + return 0; }
static int snd_hdsp_reset(struct snd_pcm_substream *substream) @@ -4869,8 +4900,9 @@ static const struct snd_pcm_ops snd_hdsp_playback_ops = { .prepare = snd_hdsp_prepare, .trigger = snd_hdsp_trigger, .pointer = snd_hdsp_hw_pointer, - .copy = snd_hdsp_playback_copy, - .silence = snd_hdsp_hw_silence, + .copy_user = snd_hdsp_playback_copy, + .copy_kernel = snd_hdsp_playback_copy_kernel, + .fill_silence = snd_hdsp_hw_silence, };
static const struct snd_pcm_ops snd_hdsp_capture_ops = { @@ -4881,7 +4913,8 @@ static const struct snd_pcm_ops snd_hdsp_capture_ops = { .prepare = snd_hdsp_prepare, .trigger = snd_hdsp_trigger, .pointer = snd_hdsp_hw_pointer, - .copy = snd_hdsp_capture_copy, + .copy_user = snd_hdsp_capture_copy, + .copy_kernel = snd_hdsp_capture_copy_kernel, };
static int snd_hdsp_create_hwdep(struct snd_card *card, struct hdsp *hdsp)
Replace the copy and the silence ops with the new PCM ops. For simplifying the code a bit, two local helpers are introduced here: get_bpos() and playback_copy_ack().
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/isa/gus/gus_pcm.c | 97 ++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 38 deletions(-)
diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c index 0cc3f272edf1..b9f6dcbef889 100644 --- a/sound/isa/gus/gus_pcm.c +++ b/sound/isa/gus/gus_pcm.c @@ -353,26 +353,25 @@ static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf, return 0; }
-static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream, - int voice, - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) +static int get_bpos(struct gus_pcm_private *pcmp, int voice, unsigned int pos, + unsigned int len) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct gus_pcm_private *pcmp = runtime->private_data; - struct snd_gus_card *gus = pcmp->gus; - unsigned int bpos, len; - int w16, invert; - - bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); - len = samples_to_bytes(runtime, count); + unsigned int bpos = pos + (voice * (pcmp->dma_size / 2)); if (snd_BUG_ON(bpos > pcmp->dma_size)) return -EIO; if (snd_BUG_ON(bpos + len > pcmp->dma_size)) return -EIO; - if (copy_from_user(runtime->dma_area + bpos, src, len)) - return -EFAULT; + return bpos; +} + +static int playback_copy_ack(struct snd_pcm_substream *substream, + unsigned int bpos, unsigned int len) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct gus_pcm_private *pcmp = runtime->private_data; + struct snd_gus_card *gus = pcmp->gus; + int w16, invert; + if (len > 32) return snd_gf1_pcm_block_change(substream, bpos, pcmp->memory + bpos, len); @@ -383,33 +382,54 @@ static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream, pcmp->memory + bpos, len, w16, invert); }
+static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream, + int voice, unsigned long pos, + void __user *src, unsigned long count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct gus_pcm_private *pcmp = runtime->private_data; + unsigned int len = count; + int bpos; + + bpos = get_bpos(pcmp, voice, pos, len); + if (bpos < 0) + return pos; + if (copy_from_user(runtime->dma_area + bpos, src, len)) + return -EFAULT; + return playback_copy_ack(substream, bpos, len); +} + +static int snd_gf1_pcm_playback_copy_kernel(struct snd_pcm_substream *substream, + int voice, unsigned long pos, + void *src, unsigned long count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct gus_pcm_private *pcmp = runtime->private_data; + unsigned int len = count; + int bpos; + + bpos = get_bpos(pcmp, voice, pos, len); + if (bpos < 0) + return pos; + memcpy(runtime->dma_area + bpos, src, len); + return playback_copy_ack(substream, bpos, len); +} + static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream, - int voice, - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int voice, unsigned long pos, + unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; struct gus_pcm_private *pcmp = runtime->private_data; - struct snd_gus_card *gus = pcmp->gus; - unsigned int bpos, len; - int w16, invert; + unsigned int len = count; + int bpos; - bpos = samples_to_bytes(runtime, pos) + (voice * (pcmp->dma_size / 2)); - len = samples_to_bytes(runtime, count); - if (snd_BUG_ON(bpos > pcmp->dma_size)) - return -EIO; - if (snd_BUG_ON(bpos + len > pcmp->dma_size)) - return -EIO; + bpos = get_bpos(pcmp, voice, pos, len); + if (bpos < 0) + return pos; snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos, - count); - if (len > 32) - return snd_gf1_pcm_block_change(substream, bpos, - pcmp->memory + bpos, len); - - w16 = (snd_pcm_format_width(runtime->format) == 16); - invert = snd_pcm_format_unsigned(runtime->format); - return snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos, - pcmp->memory + bpos, len, w16, invert); + bytes_to_samples(runtime, count)); + return playback_copy_ack(substream, bpos, len); }
static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream, @@ -831,8 +851,9 @@ static struct snd_pcm_ops snd_gf1_pcm_playback_ops = { .prepare = snd_gf1_pcm_playback_prepare, .trigger = snd_gf1_pcm_playback_trigger, .pointer = snd_gf1_pcm_playback_pointer, - .copy = snd_gf1_pcm_playback_copy, - .silence = snd_gf1_pcm_playback_silence, + .copy_user = snd_gf1_pcm_playback_copy, + .copy_kernel = snd_gf1_pcm_playback_copy_kernel, + .fill_silence = snd_gf1_pcm_playback_silence, };
static struct snd_pcm_ops snd_gf1_pcm_capture_ops = {
Replace the copy and the silence ops with the new PCM ops. For avoiding the code redundancy, slightly hackish macros are introduced.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/isa/sb/emu8000_pcm.c | 190 ++++++++++++++++++++++++++------------------- 1 file changed, 109 insertions(+), 81 deletions(-)
diff --git a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c index c480024422af..2ee8d67871ec 100644 --- a/sound/isa/sb/emu8000_pcm.c +++ b/sound/isa/sb/emu8000_pcm.c @@ -422,121 +422,148 @@ do { \ return -EAGAIN;\ } while (0)
+enum { + COPY_USER, COPY_KERNEL, FILL_SILENCE, +}; + +#define GET_VAL(sval, buf, mode) \ + do { \ + switch (mode) { \ + case FILL_SILENCE: \ + sval = 0; \ + break; \ + case COPY_KERNEL: \ + sval = *buf++; \ + break; \ + default: \ + if (get_user(sval, (unsigned short __user *)buf)) \ + return -EFAULT; \ + buf++; \ + break; \ + } \ + } while (0)
#ifdef USE_NONINTERLEAVE -/* copy one channel block */ -static int emu8k_transfer_block(struct snd_emu8000 *emu, int offset, unsigned short *buf, int count) -{ - EMU8000_SMALW_WRITE(emu, offset); - while (count > 0) { - unsigned short sval; - CHECK_SCHEDULER(); - if (get_user(sval, buf)) - return -EFAULT; - EMU8000_SMLD_WRITE(emu, sval); - buf++; - count--; - } - return 0; -}
+#define LOOP_WRITE(rec, offset, _buf, count, mode) \ + do { \ + struct snd_emu8000 *emu = (rec)->emu; \ + unsigned short *buf = (unsigned short *)(_buf); \ + snd_emu8000_write_wait(emu, 1); \ + EMU8000_SMALW_WRITE(emu, offset); \ + while (count > 0) { \ + unsigned short sval; \ + CHECK_SCHEDULER(); \ + GET_VAL(sval, buf, mode); \ + EMU8000_SMLD_WRITE(emu, sval); \ + count--; \ + } \ + } while (0) + +/* copy one channel block */ static int emu8k_pcm_copy(struct snd_pcm_substream *subs, - int voice, - snd_pcm_uframes_t pos, - void *src, - snd_pcm_uframes_t count) + int voice, unsigned long pos, + void __user *src, unsigned long count) { struct snd_emu8k_pcm *rec = subs->runtime->private_data; - struct snd_emu8000 *emu = rec->emu;
- snd_emu8000_write_wait(emu, 1); - return emu8k_transfer_block(emu, pos + rec->loop_start[voice], src, - count); + /* convert to word unit */ + pos = (pos << 1) + rec->loop_start[voice]; + count <<= 1; + LOOP_WRITE(rec, pos, src, count, COPY_UESR); + return 0; }
-/* make a channel block silence */ -static int emu8k_silence_block(struct snd_emu8000 *emu, int offset, int count) +static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs, + int voice, unsigned long pos, + void *src, unsigned long count) { - EMU8000_SMALW_WRITE(emu, offset); - while (count > 0) { - CHECK_SCHEDULER(); - EMU8000_SMLD_WRITE(emu, 0); - count--; - } + struct snd_emu8k_pcm *rec = subs->runtime->private_data; + + /* convert to word unit */ + pos = (pos << 1) + rec->loop_start[voice]; + count <<= 1; + LOOP_WRITE(rec, pos, src, count, COPY_KERNEL); return 0; }
+/* make a channel block silence */ static int emu8k_pcm_silence(struct snd_pcm_substream *subs, - int voice, - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int voice, unsigned long pos, unsigned long count) { struct snd_emu8k_pcm *rec = subs->runtime->private_data; - struct snd_emu8000 *emu = rec->emu;
- snd_emu8000_write_wait(emu, 1); - return emu8k_silence_block(emu, pos + rec->loop_start[voice], count); + /* convert to word unit */ + pos = (pos << 1) + rec->loop_start[voice]; + count <<= 1; + LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE); + return 0; }
#else /* interleave */
+#define LOOP_WRITE(rec, pos, _buf, count, mode) \ + do { \ + struct snd_emu8000 *emu = rec->emu; \ + unsigned short *buf = (unsigned short *)(_buf); \ + snd_emu8000_write_wait(emu, 1); \ + EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); \ + if (rec->voices > 1) \ + EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); \ + while (count > 0) { \ + unsigned short sval; \ + CHECK_SCHEDULER(); \ + GET_VAL(sval, buf, mode); \ + EMU8000_SMLD_WRITE(emu, sval); \ + if (rec->voices > 1) { \ + CHECK_SCHEDULER(); \ + GET_VAL(sval, buf, mode); \ + EMU8000_SMRD_WRITE(emu, sval); \ + } \ + count--; \ + } \ + } while (0) + + /* * copy the interleaved data can be done easily by using * DMA "left" and "right" channels on emu8k engine. */ static int emu8k_pcm_copy(struct snd_pcm_substream *subs, - int voice, - snd_pcm_uframes_t pos, - void __user *src, - snd_pcm_uframes_t count) + int voice, unsigned long pos, + void __user *src, unsigned long count) { struct snd_emu8k_pcm *rec = subs->runtime->private_data; - struct snd_emu8000 *emu = rec->emu; - unsigned short __user *buf = src;
- snd_emu8000_write_wait(emu, 1); - EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); - if (rec->voices > 1) - EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); - - while (count-- > 0) { - unsigned short sval; - CHECK_SCHEDULER(); - if (get_user(sval, buf)) - return -EFAULT; - EMU8000_SMLD_WRITE(emu, sval); - buf++; - if (rec->voices > 1) { - CHECK_SCHEDULER(); - if (get_user(sval, buf)) - return -EFAULT; - EMU8000_SMRD_WRITE(emu, sval); - buf++; - } - } + /* convert to frames */ + pos = bytes_to_frames(subs->runtime, pos); + count = bytes_to_frames(subs->runtime, count); + LOOP_WRITE(rec, pos, src, count, COPY_USER); + return 0; +} + +static int emu8k_pcm_copy_kernel(struct snd_pcm_substream *subs, + int voice, unsigned long pos, + void *src, unsigned long count) +{ + struct snd_emu8k_pcm *rec = subs->runtime->private_data; + + /* convert to frames */ + pos = bytes_to_frames(subs->runtime, pos); + count = bytes_to_frames(subs->runtime, count); + LOOP_WRITE(rec, pos, src, count, COPY_KERNEL); return 0; }
static int emu8k_pcm_silence(struct snd_pcm_substream *subs, - int voice, - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int voice, unsigned long pos, unsigned long count) { struct snd_emu8k_pcm *rec = subs->runtime->private_data; - struct snd_emu8000 *emu = rec->emu;
- snd_emu8000_write_wait(emu, 1); - EMU8000_SMALW_WRITE(emu, rec->loop_start[0] + pos); - if (rec->voices > 1) - EMU8000_SMARW_WRITE(emu, rec->loop_start[1] + pos); - while (count-- > 0) { - CHECK_SCHEDULER(); - EMU8000_SMLD_WRITE(emu, 0); - if (rec->voices > 1) { - CHECK_SCHEDULER(); - EMU8000_SMRD_WRITE(emu, 0); - } - } + /* convert to frames */ + pos = bytes_to_frames(subs->runtime, pos); + count = bytes_to_frames(subs->runtime, count); + LOOP_WRITE(rec, pos, NULL, count, FILL_SILENCE); return 0; } #endif @@ -652,8 +679,9 @@ static struct snd_pcm_ops emu8k_pcm_ops = { .prepare = emu8k_pcm_prepare, .trigger = emu8k_pcm_trigger, .pointer = emu8k_pcm_pointer, - .copy = emu8k_pcm_copy, - .silence = emu8k_pcm_silence, + .copy_user = emu8k_pcm_copy, + .copy_kernel = emu8k_pcm_copy_kernel, + .fill_silence = emu8k_pcm_silence, };
Replace the copy and the silence ops with the new PCM ops. Fixed also the user-space buffer copy with the proper copy_from_user*() variant.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/sh/sh_dac_audio.c | 54 +++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 24 deletions(-)
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c index 461b310c7872..c1e00ed715ee 100644 --- a/sound/sh/sh_dac_audio.c +++ b/sound/sh/sh_dac_audio.c @@ -184,23 +184,36 @@ static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return 0; }
-static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count) +static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *src, unsigned long count) { /* channel is not used (interleaved data) */ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - ssize_t b_count = frames_to_bytes(runtime , count); - ssize_t b_pos = frames_to_bytes(runtime , pos);
- if (count < 0) - return -EINVAL; + if (copy_from_user_toio(chip->data_buffer + pos, src, count)) + return -EFAULT; + chip->buffer_end = chip->data_buffer + pos + count;
- if (!count) - return 0; + if (chip->empty) { + chip->empty = 0; + dac_audio_start_timer(chip); + } + + return 0; +}
- memcpy_toio(chip->data_buffer + b_pos, src, b_count); - chip->buffer_end = chip->data_buffer + b_pos + b_count; +static int snd_sh_dac_pcm_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count) +{ + /* channel is not used (interleaved data) */ + struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + memcpy_toio(chip->data_buffer + pos, src, count); + chip->buffer_end = chip->data_buffer + pos + count;
if (chip->empty) { chip->empty = 0; @@ -211,23 +224,15 @@ static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel, }
static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) + int channel, unsigned long pos, + unsigned long count) { /* channel is not used (interleaved data) */ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - ssize_t b_count = frames_to_bytes(runtime , count); - ssize_t b_pos = frames_to_bytes(runtime , pos); - - if (count < 0) - return -EINVAL; - - if (!count) - return 0;
- memset_io(chip->data_buffer + b_pos, 0, b_count); - chip->buffer_end = chip->data_buffer + b_pos + b_count; + memset_io(chip->data_buffer + pos, 0, count); + chip->buffer_end = chip->data_buffer + pos + count;
if (chip->empty) { chip->empty = 0; @@ -256,8 +261,9 @@ static struct snd_pcm_ops snd_sh_dac_pcm_ops = { .prepare = snd_sh_dac_pcm_prepare, .trigger = snd_sh_dac_pcm_trigger, .pointer = snd_sh_dac_pcm_pointer, - .copy = snd_sh_dac_pcm_copy, - .silence = snd_sh_dac_pcm_silence, + .copy_user = snd_sh_dac_pcm_copy, + .copy_kernel = snd_sh_dac_pcm_copy_kernel, + .fill_silence = snd_sh_dac_pcm_silence, .mmap = snd_pcm_lib_mmap_iomem, };
Replace the copy and the silence ops with the new PCM ops. In AC97 and I2S-TDM mode, we need to convert back to frames, but otherwise the conversion is pretty straightforward.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/soc/blackfin/bf5xx-ac97-pcm.c | 27 +++++++++++++++++++-------- sound/soc/blackfin/bf5xx-i2s-pcm.c | 36 ++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 20 deletions(-)
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 02ad2606fa19..913e29275f4e 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -279,23 +279,33 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, return 0 ; } #else -static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, - void __user *buf, snd_pcm_uframes_t count) +static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *buf, unsigned long count) { struct snd_pcm_runtime *runtime = substream->runtime; unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1]; + struct ac97_frame *dst; + pr_debug("%s copy pos:0x%lx count:0x%lx\n", substream->stream ? "Capture" : "Playback", pos, count); + dst = (struct ac97_frame *)runtime->dma_area + + bytes_to_frames(runtime, pos); + count = bytes_to_frames(runtime, count);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - bf5xx_pcm_to_ac97((struct ac97_frame *)runtime->dma_area + pos, - (__u16 *)buf, count, chan_mask); + bf5xx_pcm_to_ac97(dst, buf, count, chan_mask); else - bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos, - (__u16 *)buf, count); + bf5xx_ac97_to_pcm(dst, buf, count); return 0; } + +static int bf5xx_pcm_copy_user(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *buf, unsigned long count) +{ + return bf5xx_pcm_copy(substream, channel, pos, (void *)buf, count); +} #endif
static struct snd_pcm_ops bf5xx_pcm_ac97_ops = { @@ -309,7 +319,8 @@ static struct snd_pcm_ops bf5xx_pcm_ac97_ops = { #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) .mmap = bf5xx_pcm_mmap, #else - .copy = bf5xx_pcm_copy, + .copy_user = bf5xx_pcm_copy_user, + .copy_kernel = bf5xx_pcm_copy, #endif };
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c index 6cba211da32e..470d99abf6f6 100644 --- a/sound/soc/blackfin/bf5xx-i2s-pcm.c +++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c @@ -225,8 +225,9 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, return 0 ; }
-static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) +static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *buf, unsigned long count) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; @@ -238,6 +239,8 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
if (dma_data->tdm_mode) { + pos = bytes_to_frames(runtime, pos); + count = bytes_to_frames(runtime, count); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { src = buf; dst = runtime->dma_area; @@ -269,21 +272,29 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { src = buf; dst = runtime->dma_area; - dst += frames_to_bytes(runtime, pos); + dst += pos; } else { src = runtime->dma_area; - src += frames_to_bytes(runtime, pos); + src += pos; dst = buf; }
- memcpy(dst, src, frames_to_bytes(runtime, count)); + memcpy(dst, src, count); }
return 0; }
+static int bf5xx_pcm_copy_user(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *buf, unsigned long count) +{ + return bf5xx_pcm_copy(substream, channel, pos, (void *)buf, count); +} + static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, - int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) + int channel, unsigned long pos, + unsigned long count) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; @@ -295,11 +306,11 @@ static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
if (dma_data->tdm_mode) { - offset = pos * 8 * sample_size; - samples = count * 8; + offset = bytes_to_frames(runtime, pos) * 8 * sample_size; + samples = bytes_to_frames(runtime, count) * 8; } else { - offset = frames_to_bytes(runtime, pos); - samples = count * runtime->channels; + offset = pos; + samples = bytes_to_samples(runtime, count); }
snd_pcm_format_set_silence(runtime->format, buf + offset, samples); @@ -316,8 +327,9 @@ static struct snd_pcm_ops bf5xx_pcm_i2s_ops = { .trigger = bf5xx_pcm_trigger, .pointer = bf5xx_pcm_pointer, .mmap = bf5xx_pcm_mmap, - .copy = bf5xx_pcm_copy, - .silence = bf5xx_pcm_silence, + .copy_user = bf5xx_pcm_copy_user, + .copy_kernel = bf5xx_pcm_copy, + .fill_silence = bf5xx_pcm_silence, };
static int bf5xx_pcm_i2s_new(struct snd_soc_pcm_runtime *rtd)
On Thu, Jun 01, 2017 at 10:58:37PM +0200, Takashi Iwai wrote:
Replace the copy and the silence ops with the new PCM ops. In AC97 and I2S-TDM mode, we need to convert back to frames, but otherwise the conversion is pretty straightforward.
Acked-by: Mark Brown broonie@kernel.org
Replace the copy and the silence ops with the new PCM ops. The device supports only 1 channel and 8bit sample, so it's always bytes=frames, and we need no conversion of unit in the callback. Also, it's a capture stream, thus no silence is needed.
Signed-off-by: Takashi Iwai tiwai@suse.de --- drivers/media/pci/solo6x10/solo6x10-g723.c | 32 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-)
diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index 36e93540bb49..3ca947092775 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -223,9 +223,9 @@ static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) return idx * G723_FRAMES_PER_PAGE; }
-static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, - snd_pcm_uframes_t pos, void __user *dst, - snd_pcm_uframes_t count) +static int __snd_solo_pcm_copy(struct snd_pcm_substream *ss, + unsigned long pos, void *dst, + unsigned long count, bool in_kernel) { struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); struct solo_dev *solo_dev = solo_pcm->solo_dev; @@ -242,16 +242,31 @@ static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, if (err) return err;
- err = copy_to_user(dst + (i * G723_PERIOD_BYTES), - solo_pcm->g723_buf, G723_PERIOD_BYTES); - - if (err) + if (in_kernel) + memcpy(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES); + else if (copy_to_user((void __user *)dst, + solo_pcm->g723_buf, G723_PERIOD_BYTES)) return -EFAULT; + dst += G723_PERIOD_BYTES; }
return 0; }
+static int snd_solo_pcm_copy_user(struct snd_pcm_substream *ss, int channel, + unsigned long pos, void __user *dst, + unsigned long count) +{ + return __snd_solo_pcm_copy(ss, pos, (void *)dst, count, false); +} + +static int snd_solo_pcm_copy_kernel(struct snd_pcm_substream *ss, int channel, + unsigned long pos, void *dst, + unsigned long count) +{ + return __snd_solo_pcm_copy(ss, pos, dst, count, true); +} + static const struct snd_pcm_ops snd_solo_pcm_ops = { .open = snd_solo_pcm_open, .close = snd_solo_pcm_close, @@ -261,7 +276,8 @@ static const struct snd_pcm_ops snd_solo_pcm_ops = { .prepare = snd_solo_pcm_prepare, .trigger = snd_solo_pcm_trigger, .pointer = snd_solo_pcm_pointer, - .copy = snd_solo_pcm_copy, + .copy_user = snd_solo_pcm_copy_user, + .copy_kernel = snd_solo_pcm_copy_kernel, };
static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol,
On 06/01/2017 10:58 PM, Takashi Iwai wrote:
Replace the copy and the silence ops with the new PCM ops. The device supports only 1 channel and 8bit sample, so it's always bytes=frames, and we need no conversion of unit in the callback. Also, it's a capture stream, thus no silence is needed.
Signed-off-by: Takashi Iwai tiwai@suse.de
Acked-by: Hans Verkuil hans.verkuil@cisco.com
Thanks,
Hans
drivers/media/pci/solo6x10/solo6x10-g723.c | 32 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-)
diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index 36e93540bb49..3ca947092775 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -223,9 +223,9 @@ static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) return idx * G723_FRAMES_PER_PAGE; }
-static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel,
snd_pcm_uframes_t pos, void __user *dst,
snd_pcm_uframes_t count)
+static int __snd_solo_pcm_copy(struct snd_pcm_substream *ss,
unsigned long pos, void *dst,
{ struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); struct solo_dev *solo_dev = solo_pcm->solo_dev;unsigned long count, bool in_kernel)
@@ -242,16 +242,31 @@ static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, if (err) return err;
err = copy_to_user(dst + (i * G723_PERIOD_BYTES),
solo_pcm->g723_buf, G723_PERIOD_BYTES);
if (err)
if (in_kernel)
memcpy(dst, solo_pcm->g723_buf, G723_PERIOD_BYTES);
else if (copy_to_user((void __user *)dst,
solo_pcm->g723_buf, G723_PERIOD_BYTES)) return -EFAULT;
dst += G723_PERIOD_BYTES;
}
return 0; }
+static int snd_solo_pcm_copy_user(struct snd_pcm_substream *ss, int channel,
unsigned long pos, void __user *dst,
unsigned long count)
+{
- return __snd_solo_pcm_copy(ss, pos, (void *)dst, count, false);
+}
+static int snd_solo_pcm_copy_kernel(struct snd_pcm_substream *ss, int channel,
unsigned long pos, void *dst,
unsigned long count)
+{
- return __snd_solo_pcm_copy(ss, pos, dst, count, true);
+}
- static const struct snd_pcm_ops snd_solo_pcm_ops = { .open = snd_solo_pcm_open, .close = snd_solo_pcm_close,
@@ -261,7 +276,8 @@ static const struct snd_pcm_ops snd_solo_pcm_ops = { .prepare = snd_solo_pcm_prepare, .trigger = snd_solo_pcm_trigger, .pointer = snd_solo_pcm_pointer,
- .copy = snd_solo_pcm_copy,
.copy_user = snd_solo_pcm_copy_user,
.copy_kernel = snd_solo_pcm_copy_kernel, };
static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol,
Now that all users of old copy and silence ops have been converted to the new PCM ops, the old stuff can be retired and go away.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/pcm.h | 5 ----- sound/core/pcm_lib.c | 38 +------------------------------------- sound/soc/soc-pcm.c | 2 -- 3 files changed, 1 insertion(+), 44 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index a065415191d8..953ebfc83184 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -78,11 +78,6 @@ struct snd_pcm_ops { struct timespec *system_ts, struct timespec *audio_ts, struct snd_pcm_audio_tstamp_config *audio_tstamp_config, struct snd_pcm_audio_tstamp_report *audio_tstamp_report); - int (*copy)(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, - void __user *buf, snd_pcm_uframes_t count); - int (*silence)(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, snd_pcm_uframes_t count); int (*fill_silence)(struct snd_pcm_substream *substream, int channel, unsigned long pos, unsigned long bytes); int (*copy_user)(struct snd_pcm_substream *substream, int channel, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 9334fc2c20c8..0db8d4e0fca2 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -116,9 +116,6 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram frames_to_bytes(runtime, ofs), frames_to_bytes(runtime, transfer)); snd_BUG_ON(err < 0); - } else if (substream->ops->silence) { - err = substream->ops->silence(substream, -1, ofs, transfer); - snd_BUG_ON(err < 0); } else { hwbuf = runtime->dma_area + frames_to_bytes(runtime, ofs); snd_pcm_format_set_silence(runtime->format, hwbuf, transfer * runtime->channels); @@ -133,11 +130,6 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram samples_to_bytes(runtime, transfer)); snd_BUG_ON(err < 0); } - } else if (substream->ops->silence) { - for (c = 0; c < channels; ++c) { - 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) { @@ -2013,9 +2005,6 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); if (err < 0) return err; - } else 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))) @@ -2137,8 +2126,7 @@ 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_user && !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; @@ -2198,19 +2186,6 @@ static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, if (err < 0) return err; } - } else 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 { - 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; @@ -2264,9 +2239,6 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); if (err < 0) return err; - } else 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_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) @@ -2428,14 +2400,6 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, if (err < 0) return err; } - } else if (substream->ops->copy) { - for (c = 0; c < channels; ++c, ++bufs) { - 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) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 8867ed9e5f56..dcc5ece08668 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2746,8 +2746,6 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.copy_user = platform->driver->ops->copy_user; rtd->ops.copy_kernel = platform->driver->ops->copy_kernel; rtd->ops.fill_silence = platform->driver->ops->fill_silence; - rtd->ops.copy = platform->driver->ops->copy; - rtd->ops.silence = platform->driver->ops->silence; rtd->ops.page = platform->driver->ops->page; rtd->ops.mmap = platform->driver->ops->mmap; }
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/pcm_lib.c | 81 +++++++++++++++++++--------------------------------- 1 file changed, 29 insertions(+), 52 deletions(-)
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 0db8d4e0fca2..e4f5c43b6448 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2017,6 +2017,22 @@ typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwof unsigned long data, unsigned int off, snd_pcm_uframes_t size);
+static int pcm_accessible_state(struct snd_pcm_runtime *runtime) +{ + 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; + } +} + static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, unsigned long data, snd_pcm_uframes_t size, @@ -2033,21 +2049,9 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, return 0;
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; + err = pcm_accessible_state(runtime); + if (err < 0) goto _end_unlock; - }
runtime->twake = runtime->control->avail_min ? : 1; if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) @@ -2083,16 +2087,9 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 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; @@ -2263,27 +2260,14 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, return 0;
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 (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; @@ -2327,16 +2311,9 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, 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;
Just shuffle the codes, without any change otherwise.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/pcm_lib.c | 212 +++++++++++++++++++++++++-------------------------- 1 file changed, 106 insertions(+), 106 deletions(-)
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index e4f5c43b6448..1f5251cca607 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1991,6 +1991,10 @@ static int wait_for_avail(struct snd_pcm_substream *substream, return err; } +typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, + unsigned long data, unsigned int off, + snd_pcm_uframes_t size); + static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, @@ -2013,9 +2017,68 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, 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 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) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + void __user **bufs = (void __user **)data; + int channels = runtime->channels; + char __user *buf; + int c; + + if (substream->ops->copy_user) { + hwoff = samples_to_bytes(runtime, hwoff); + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + for (c = 0; c < channels; ++c, ++bufs) { + buf = *bufs + off; + if (!*bufs) { + if (snd_BUG_ON(!substream->ops->fill_silence)) + return -EINVAL; + err = substream->ops->fill_silence(substream, c, + hwoff, + frames); + } else { + err = substream->ops->copy_user(substream, c, + hwoff, buf, + frames); + } + if (err < 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; + } + } + } + return 0; +} + +/* sanity-check for read/write methods */ +static int pcm_sanity_check(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime; + if (PCM_RUNTIME_CHECK(substream)) + return -ENXIO; + runtime = substream->runtime; + 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; +}
static int pcm_accessible_state(struct snd_pcm_runtime *runtime) { @@ -2116,20 +2179,6 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; }
-/* sanity-check for read/write methods */ -static int pcm_sanity_check(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime; - if (PCM_RUNTIME_CHECK(substream)) - return -ENXIO; - runtime = substream->runtime; - 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) { struct snd_pcm_runtime *runtime; @@ -2151,55 +2200,6 @@ snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const v
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) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - void __user **bufs = (void __user **)data; - int channels = runtime->channels; - char __user *buf; - int c; - - if (substream->ops->copy_user) { - hwoff = samples_to_bytes(runtime, hwoff); - off = samples_to_bytes(runtime, off); - frames = samples_to_bytes(runtime, frames); - for (c = 0; c < channels; ++c, ++bufs) { - buf = *bufs + off; - if (!*bufs) { - if (snd_BUG_ON(!substream->ops->fill_silence)) - return -EINVAL; - err = substream->ops->fill_silence(substream, c, - hwoff, - frames); - } else { - err = substream->ops->copy_user(substream, c, - hwoff, buf, - frames); - } - if (err < 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; - } - } - } - return 0; -} - snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, void __user **bufs, snd_pcm_uframes_t frames) @@ -2244,6 +2244,46 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, return 0; }
+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; + char __user *buf; + char *hwbuf; + int c; + + if (substream->ops->copy_user) { + hwoff = samples_to_bytes(runtime, hwoff); + off = samples_to_bytes(runtime, off); + frames = samples_to_bytes(runtime, frames); + for (c = 0; c < channels; ++c, ++bufs) { + if (!*bufs) + continue; + err = substream->ops->copy_user(substream, c, hwoff, + *bufs + off, frames); + if (err < 0) + return err; + } + } else { + snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; + for (c = 0; c < channels; ++c, ++bufs) { + 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; +} + static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, unsigned long data, snd_pcm_uframes_t size, @@ -2352,46 +2392,6 @@ snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __u
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; - char __user *buf; - char *hwbuf; - int c; - - if (substream->ops->copy_user) { - hwoff = samples_to_bytes(runtime, hwoff); - off = samples_to_bytes(runtime, off); - frames = samples_to_bytes(runtime, frames); - for (c = 0; c < channels; ++c, ++bufs) { - if (!*bufs) - continue; - err = substream->ops->copy_user(substream, c, hwoff, - *bufs + off, frames); - if (err < 0) - return err; - } - } else { - snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; - for (c = 0; c < channels; ++c, ++bufs) { - 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)
Make snd_pcm_lib_read() and *_write() static inline functions that call the common helper functions directly. This reduces a slight amount of codes, and at the same time, it's a preparation for the further cleanups / fixes.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/pcm.h | 43 +++++++++++--- sound/core/pcm_lib.c | 156 ++++++++++++++++++--------------------------------- 2 files changed, 89 insertions(+), 110 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 953ebfc83184..cb4eff8ffd2e 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1088,15 +1088,40 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, 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); void snd_pcm_period_elapsed(struct snd_pcm_substream *substream); -snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, - const void __user *buf, - snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, - void __user *buf, snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, - void __user **bufs, snd_pcm_uframes_t frames); -snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, - void __user **bufs, snd_pcm_uframes_t frames); +snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, + void *buf, bool interleaved, + snd_pcm_uframes_t frames); +snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, + void *buf, bool interleaved, + snd_pcm_uframes_t frames); + +static inline snd_pcm_sframes_t +snd_pcm_lib_write(struct snd_pcm_substream *substream, + const void __user *buf, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_write(substream, (void *)buf, true, frames); +} + +static inline snd_pcm_sframes_t +snd_pcm_lib_read(struct snd_pcm_substream *substream, + void __user *buf, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_read(substream, (void *)buf, true, frames); +} + +static inline snd_pcm_sframes_t +snd_pcm_lib_writev(struct snd_pcm_substream *substream, + void __user **bufs, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_write(substream, (void *)bufs, false, frames); +} + +static inline snd_pcm_sframes_t +snd_pcm_lib_readv(struct snd_pcm_substream *substream, + void __user **bufs, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_read(substream, (void *)bufs, false, frames); +}
extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 1f5251cca607..1bd7244324d5 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1992,12 +1992,12 @@ static int wait_for_avail(struct snd_pcm_substream *substream, } typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, - unsigned long data, unsigned int off, + void *data, unsigned int off, snd_pcm_uframes_t size);
static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, - unsigned long data, unsigned int off, + void *data, unsigned int off, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2019,7 +2019,7 @@ static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream,
static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, - unsigned long data, unsigned int off, + void *data, unsigned int off, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2096,21 +2096,39 @@ static int pcm_accessible_state(struct snd_pcm_runtime *runtime) } }
-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) +snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, + void *data, bool interleaved, + snd_pcm_uframes_t size) { 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; + transfer_f transfer; + bool nonblock; + int err; + + err = pcm_sanity_check(substream); + if (err < 0) + return err; + runtime = substream->runtime; + + if (interleaved) { + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && + runtime->channels > 1) + return -EINVAL; + transfer = snd_pcm_lib_write_transfer; + } else { + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + transfer = snd_pcm_lib_writev_transfer; + }
if (size == 0) return 0;
+ nonblock = !!(substream->f_flags & O_NONBLOCK); + snd_pcm_stream_lock_irq(substream); err = pcm_accessible_state(runtime); if (err < 0) @@ -2178,53 +2196,11 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(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_write(struct snd_pcm_substream *substream, const 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 && - runtime->channels > 1) - return -EINVAL; - return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock, - snd_pcm_lib_write_transfer); -} - -EXPORT_SYMBOL(snd_pcm_lib_write); - -snd_pcm_sframes_t snd_pcm_lib_writev(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; - 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); +EXPORT_SYMBOL(__snd_pcm_lib_write);
static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, - unsigned long data, unsigned int off, + void *data, unsigned int off, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2246,7 +2222,7 @@ static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream,
static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, - unsigned long data, unsigned int off, + void *data, unsigned int off, snd_pcm_uframes_t frames) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -2284,21 +2260,39 @@ static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, 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) +snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, + void *data, bool interleaved, + snd_pcm_uframes_t size) { 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; + transfer_f transfer; + bool nonblock; + int err; + + err = pcm_sanity_check(substream); + if (err < 0) + return err; + runtime = substream->runtime; + + if (interleaved) { + if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && + runtime->channels > 1) + return -EINVAL; + transfer = snd_pcm_lib_read_transfer; + } else { + if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + return -EINVAL; + transfer = snd_pcm_lib_readv_transfer; + }
if (size == 0) return 0;
+ nonblock = !!(substream->f_flags & O_NONBLOCK); + snd_pcm_stream_lock_irq(substream); err = pcm_accessible_state(runtime); if (err < 0) @@ -2373,47 +2367,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); - -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_read);
/* * standard channel mapping helpers
This patch proceeds more abstraction of PCM read/write loop codes.
For both interleaved and non-interleaved transfers, the same copy or silence transfer code (which is defined as pcm_transfer_f) is used now. This became possible since we switched to byte size to copy_* and fill_silence ops argument instead of frames.
And, for both read and write, we can use the same copy function (which is defined as pcm_copy_f), just depending on whether interleaved or non-interleaved mode.
The transfer function is determined at the beginning of the loop, depending on whether the driver gives the specific copy ops or it's the standard read/write.
Another bonus by this change is that we now guarantee the silencing behavior when NULL buffer is passed to write helpers. It'll simplify some codes later.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/pcm_lib.c | 254 +++++++++++++++++++++++++-------------------------- 1 file changed, 123 insertions(+), 131 deletions(-)
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 1bd7244324d5..eb9cbc2d9d7b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1991,77 +1991,100 @@ static int wait_for_avail(struct snd_pcm_substream *substream, return err; } -typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff, - void *data, unsigned int off, - snd_pcm_uframes_t size); +typedef int (*pcm_transfer_f)(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes);
-static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - void *data, unsigned int off, - snd_pcm_uframes_t frames) +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) +{ + return runtime->dma_area + hwoff + + channel * (runtime->dma_bytes / runtime->channels); +} + +/* default copy_user ops for write */ +static int default_write_copy_user(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void __user *buf, unsigned long bytes) +{ + if (copy_from_user(get_dma_ptr(substream->runtime, channel, hwoff), + buf, bytes)) + return -EFAULT; + 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; - int err; - char __user *buf = (char __user *) data + frames_to_bytes(runtime, off); - if (substream->ops->copy_user) { - hwoff = frames_to_bytes(runtime, hwoff); - frames = frames_to_bytes(runtime, frames); - err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); - if (err < 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; - } + + 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_format_set_silence(runtime->format, + get_dma_ptr(runtime, channel, hwoff), + bytes_to_samples(runtime, bytes)); return 0; } - -static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - void *data, unsigned int off, - snd_pcm_uframes_t frames) + +/* 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 err; - void __user **bufs = (void __user **)data; int channels = runtime->channels; - char __user *buf; - int c; - - if (substream->ops->copy_user) { - hwoff = samples_to_bytes(runtime, hwoff); - off = samples_to_bytes(runtime, off); - frames = samples_to_bytes(runtime, frames); - for (c = 0; c < channels; ++c, ++bufs) { - buf = *bufs + off; - if (!*bufs) { - if (snd_BUG_ON(!substream->ops->fill_silence)) - return -EINVAL; - err = substream->ops->fill_silence(substream, c, - hwoff, - frames); - } else { - err = substream->ops->copy_user(substream, c, - hwoff, buf, - frames); - } - if (err < 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; - } - } + 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; } return 0; } @@ -2104,24 +2127,33 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; snd_pcm_uframes_t avail; - transfer_f transfer; + pcm_copy_f writer; + pcm_transfer_f transfer; bool nonblock; int err;
err = pcm_sanity_check(substream); if (err < 0) return err; - runtime = substream->runtime;
if (interleaved) { if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && runtime->channels > 1) return -EINVAL; - transfer = snd_pcm_lib_write_transfer; + writer = interleaved_copy; } else { if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) return -EINVAL; - transfer = snd_pcm_lib_writev_transfer; + writer = noninterleaved_copy; + } + + if (!data) { + transfer = fill_silence; + } else { + if (substream->ops->copy_user) + transfer = (pcm_transfer_f)substream->ops->copy_user; + else + transfer = default_write_copy_user; }
if (size == 0) @@ -2164,7 +2196,8 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, 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; @@ -2198,65 +2231,15 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, } EXPORT_SYMBOL(__snd_pcm_lib_write);
-static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - void *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_user) { - hwoff = frames_to_bytes(runtime, hwoff); - frames = frames_to_bytes(runtime, frames); - err = substream->ops->copy_user(substream, 0, hwoff, buf, frames); - if (err < 0) - return err; - } else { - char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); - if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) - return -EFAULT; - } - return 0; -} - -static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, - unsigned int hwoff, - void *data, unsigned int off, - snd_pcm_uframes_t frames) +/* default copy_user ops for read */ +static int default_read_copy_user(struct snd_pcm_substream *substream, + int channel, unsigned long hwoff, + void *buf, unsigned long bytes) { - struct snd_pcm_runtime *runtime = substream->runtime; - int err; - void __user **bufs = (void __user **)data; - int channels = runtime->channels; - char __user *buf; - char *hwbuf; - int c; - - if (substream->ops->copy_user) { - hwoff = samples_to_bytes(runtime, hwoff); - off = samples_to_bytes(runtime, off); - frames = samples_to_bytes(runtime, frames); - for (c = 0; c < channels; ++c, ++bufs) { - if (!*bufs) - continue; - err = substream->ops->copy_user(substream, c, hwoff, - *bufs + off, frames); - if (err < 0) - return err; - } - } else { - snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels; - for (c = 0; c < channels; ++c, ++bufs) { - 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; - } - } + if (copy_to_user((void __user *)buf, + get_dma_ptr(substream->runtime, channel, hwoff), + bytes)) + return -EFAULT; return 0; }
@@ -2268,26 +2251,34 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; snd_pcm_uframes_t avail; - transfer_f transfer; + pcm_copy_f reader; + pcm_transfer_f transfer; bool nonblock; int err;
err = pcm_sanity_check(substream); if (err < 0) return err; - runtime = substream->runtime; + + if (!data) + return -EINVAL;
if (interleaved) { if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && runtime->channels > 1) return -EINVAL; - transfer = snd_pcm_lib_read_transfer; + reader = interleaved_copy; } else { if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) return -EINVAL; - transfer = snd_pcm_lib_readv_transfer; + reader = noninterleaved_copy; }
+ if (substream->ops->copy_user) + transfer = (pcm_transfer_f)substream->ops->copy_user; + else + transfer = default_read_copy_user; + if (size == 0) return 0;
@@ -2341,7 +2332,8 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, 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 = reader(substream, appl_ofs, data, offset, frames, + transfer); snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end_unlock;
Both __snd_pcm_lib_read() and __snd_pcm_write() functions have almost the same code to loop over samples. For simplification, this patch unifies both as the single helper, __snd_pcm_lib_xfer().
Other than that, there should be no functional change by this patch.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/pcm.h | 13 ++-- sound/core/pcm_lib.c | 184 +++++++++++++-------------------------------------- 2 files changed, 51 insertions(+), 146 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index cb4eff8ffd2e..173c6a6ebf35 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1088,10 +1088,7 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, 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); void snd_pcm_period_elapsed(struct snd_pcm_substream *substream); -snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, - void *buf, bool interleaved, - snd_pcm_uframes_t frames); -snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, +snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, void *buf, bool interleaved, snd_pcm_uframes_t frames);
@@ -1099,28 +1096,28 @@ static inline snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t frames) { - return __snd_pcm_lib_write(substream, (void *)buf, true, frames); + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames); }
static inline snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t frames) { - return __snd_pcm_lib_read(substream, (void *)buf, true, frames); + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames); }
static inline snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, void __user **bufs, snd_pcm_uframes_t frames) { - return __snd_pcm_lib_write(substream, (void *)bufs, false, frames); + return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames); }
static inline snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, void __user **bufs, snd_pcm_uframes_t frames) { - return __snd_pcm_lib_read(substream, (void *)bufs, false, frames); + return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames); }
extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index eb9cbc2d9d7b..f15460eaf8b5 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -2006,13 +2006,13 @@ static void *get_dma_ptr(struct snd_pcm_runtime *runtime, channel * (runtime->dma_bytes / runtime->channels); }
-/* default copy_user ops for write */ -static int default_write_copy_user(struct snd_pcm_substream *substream, - int channel, unsigned long hwoff, - void __user *buf, unsigned long bytes) +/* 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), - buf, bytes)) + (void __user *)buf, bytes)) return -EFAULT; return 0; } @@ -2038,6 +2038,18 @@ static int fill_silence(struct snd_pcm_substream *substream, int channel, return 0; }
+/* 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; +} + /* call transfer function with the converted pointers and sizes; * for interleaved mode, it's one shot for all samples */ @@ -2119,9 +2131,10 @@ static int pcm_accessible_state(struct snd_pcm_runtime *runtime) } }
-snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, - void *data, bool interleaved, - snd_pcm_uframes_t size) +/* 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) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t xfer = 0; @@ -2130,12 +2143,14 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, 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;
+ is_playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; if (interleaved) { if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && runtime->channels > 1) @@ -2148,12 +2163,16 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream, }
if (!data) { - transfer = fill_silence; + if (is_playback) + transfer = fill_silence; + else + return -EINVAL; } else { if (substream->ops->copy_user) transfer = (pcm_transfer_f)substream->ops->copy_user; else - transfer = default_write_copy_user; + transfer = is_playback ? + default_write_copy : default_read_copy; }
if (size == 0) @@ -2166,129 +2185,8 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *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_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 = writer(substream, appl_ofs, data, offset, frames, - transfer); - snd_pcm_stream_lock_irq(substream); - if (err < 0) - goto _end_unlock; - err = pcm_accessible_state(runtime); - if (err < 0) - goto _end_unlock; - 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); - - 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; - } - } - _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; -} -EXPORT_SYMBOL(__snd_pcm_lib_write); - -/* default copy_user ops for read */ -static int default_read_copy_user(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; -} - -snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, - void *data, bool interleaved, - snd_pcm_uframes_t size) -{ - 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 reader; - pcm_transfer_f transfer; - bool nonblock; - int err; - - err = pcm_sanity_check(substream); - if (err < 0) - return err; - - if (!data) - return -EINVAL; - - if (interleaved) { - if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && - runtime->channels > 1) - return -EINVAL; - reader = interleaved_copy; - } else { - if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) - return -EINVAL; - reader = noninterleaved_copy; - } - - if (substream->ops->copy_user) - transfer = (pcm_transfer_f)substream->ops->copy_user; - else - transfer = default_read_copy_user; - - if (size == 0) - return 0; - - nonblock = !!(substream->f_flags & O_NONBLOCK); - - snd_pcm_stream_lock_irq(substream); - err = pcm_accessible_state(runtime); - if (err < 0) - goto _end_unlock; - if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && + if (!is_playback && + runtime->status->state == SNDRV_PCM_STATE_PREPARED && size >= runtime->start_threshold) { err = snd_pcm_start(substream); if (err < 0) @@ -2298,13 +2196,16 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, 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; } @@ -2332,7 +2233,7 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); - err = reader(substream, appl_ofs, data, offset, frames, + err = writer(substream, appl_ofs, data, offset, frames, transfer); snd_pcm_stream_lock_irq(substream); if (err < 0) @@ -2351,6 +2252,13 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, 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; @@ -2359,7 +2267,7 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *substream, snd_pcm_stream_unlock_irq(substream); return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } -EXPORT_SYMBOL(__snd_pcm_lib_read); +EXPORT_SYMBOL(__snd_pcm_lib_xfer);
/* * standard channel mapping helpers
Use the existing silence helper codes for simplification.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/pcm_lib.c | 50 ++++++++++++++++++++------------------------------ 1 file changed, 20 insertions(+), 30 deletions(-)
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index f15460eaf8b5..a592d3308474 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -42,6 +42,9 @@ #define trace_hw_ptr_error(substream, reason) #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,7 +58,6 @@ 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; - char *hwbuf; int err;
if (runtime->silence_size < runtime->boundary) { @@ -109,35 +111,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->fill_silence) { - err = substream->ops->fill_silence(substream, 0, - frames_to_bytes(runtime, ofs), - frames_to_bytes(runtime, transfer)); - snd_BUG_ON(err < 0); - } else { - 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->fill_silence) { - for (c = 0; c < channels; ++c) { - err = substream->ops->fill_silence(substream, c, - samples_to_bytes(runtime, ofs), - samples_to_bytes(runtime, transfer)); - snd_BUG_ON(err < 0); - } - } else { - size_t dma_csize = runtime->dma_bytes / channels; - for (c = 0; c < channels; ++c) { - 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; @@ -2101,6 +2076,21 @@ static int noninterleaved_copy(struct snd_pcm_substream *substream, 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 */ static int pcm_sanity_check(struct snd_pcm_substream *substream) {
Now all materials are ready, let's allow the direct in-kernel read/write, i.e. a kernel-space buffer is passed for read or write, instead of the normal user-space buffer. This feature is used by OSS layer and UAC1 driver, for example.
The __snd_pcm_lib_xfer() takes in_kernel argument that indicates the in-kernel buffer copy. When this flag is set, another transfer code is used. It's either via copy_kernel PCM ops or the normal memcpy(), depending on the driver setup.
As external API, snd_pcm_kernel_read(), *_write() and other variants are provided.
That's all. This support is really simple because of the code refactoring until now.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/pcm.h | 38 +++++++++++++++++++++++++++++++++----- sound/core/pcm_lib.c | 26 +++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 6 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 173c6a6ebf35..e3a7269824c7 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1090,34 +1090,62 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram void snd_pcm_period_elapsed(struct snd_pcm_substream *substream); snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, void *buf, bool interleaved, - snd_pcm_uframes_t frames); + snd_pcm_uframes_t frames, bool in_kernel);
static inline snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t frames) { - return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames); + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames, false); }
static inline snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t frames) { - return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames); + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames, false); }
static inline snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream, void __user **bufs, snd_pcm_uframes_t frames) { - return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames); + return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames, false); }
static inline snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream, void __user **bufs, snd_pcm_uframes_t frames) { - return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames); + return __snd_pcm_lib_xfer(substream, (void *)bufs, false, frames, false); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_write(struct snd_pcm_substream *substream, + const void *buf, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, (void *)buf, true, frames, true); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_read(struct snd_pcm_substream *substream, + void *buf, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, buf, true, frames, true); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_writev(struct snd_pcm_substream *substream, + void **bufs, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, bufs, false, frames, true); +} + +static inline snd_pcm_sframes_t +snd_pcm_kernel_readv(struct snd_pcm_substream *substream, + void **bufs, snd_pcm_uframes_t frames) +{ + return __snd_pcm_lib_xfer(substream, bufs, false, frames, true); }
extern const struct snd_pcm_hw_constraint_list snd_pcm_known_rates; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a592d3308474..ba08b246d153 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1992,6 +1992,15 @@ static int default_write_copy(struct snd_pcm_substream *substream, return 0; }
+/* 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 @@ -2025,6 +2034,15 @@ static int default_read_copy(struct snd_pcm_substream *substream, return 0; }
+/* 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 */ @@ -2124,7 +2142,7 @@ static int pcm_accessible_state(struct snd_pcm_runtime *runtime) /* 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) + snd_pcm_uframes_t size, bool in_kernel) { struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_uframes_t xfer = 0; @@ -2157,6 +2175,12 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, 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;
With the new API to perform the in-kernel buffer copy, we can get rid of set_fs() usage in this driver, finally.
Signed-off-by: Takashi Iwai tiwai@suse.de --- drivers/usb/gadget/function/u_uac1.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c index c78c84138a28..ca88e4c0fd1e 100644 --- a/drivers/usb/gadget/function/u_uac1.c +++ b/drivers/usb/gadget/function/u_uac1.c @@ -157,7 +157,6 @@ size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) struct gaudio_snd_dev *snd = &card->playback; struct snd_pcm_substream *substream = snd->substream; struct snd_pcm_runtime *runtime = substream->runtime; - mm_segment_t old_fs; ssize_t result; snd_pcm_sframes_t frames;
@@ -174,15 +173,11 @@ size_t u_audio_playback(struct gaudio *card, void *buf, size_t count) }
frames = bytes_to_frames(runtime, count); - old_fs = get_fs(); - set_fs(KERNEL_DS); - result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames); + result = snd_pcm_kernel_write(snd->substream, buf, frames); if (result != frames) { ERROR(card, "Playback error: %d\n", (int)result); - set_fs(old_fs); goto try_again; } - set_fs(old_fs);
return 0; }
On Thu, Jun 01, 2017 at 10:58:47PM +0200, Takashi Iwai wrote:
With the new API to perform the in-kernel buffer copy, we can get rid of set_fs() usage in this driver, finally.
Signed-off-by: Takashi Iwai tiwai@suse.de
Acked-by: Greg Kroah-Hartman gregkh@linuxfoundation.org
This is the last-standing one: kill the set_fs() usage in PCM OSS layer by replacing with the new API functions to deal with the direct in-kernel buffer copying.
The code to fill the silence can be replaced even to a one-liner to pass NULL buffer instead of the manual copying.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/oss/pcm_oss.c | 77 ++++++++---------------------------------------- 1 file changed, 12 insertions(+), 65 deletions(-)
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index e306f05ce51d..2d6a825cfe88 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 */ @@ -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); @@ -1273,14 +1249,8 @@ 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_lib_xfer(substream, bufs, false, frames, + in_kernel); if (ret != -EPIPE && ret != -ESTRPIPE) break;
@@ -1313,14 +1283,8 @@ 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_lib_xfer(substream, bufs, false, frames, + in_kernel); if (ret != -EPIPE && ret != -ESTRPIPE) break; } @@ -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); /*
The snd_pcm_oss_writev3() and snd_pcm_oss_readv3() are used only in io.c with CONFIG_SND_PCM_OSS_PLUGINS=y. Add an ifdef to reduce the build of these functions.
Along with it, since they are called always for in-kernel copy, reduce the argument and call snd_pcm_kernel_writev() and *_readv() directly instead.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/oss/io.c | 4 ++-- sound/core/oss/pcm_oss.c | 12 ++++++------ sound/core/oss/pcm_plugin.h | 6 ++---- 3 files changed, 10 insertions(+), 12 deletions(-)
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/pcm_oss.c b/sound/core/oss/pcm_oss.c index 2d6a825cfe88..5e1009d959a8 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -1232,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; @@ -1249,8 +1250,7 @@ snd_pcm_sframes_t snd_pcm_oss_writev3(struct snd_pcm_substream *substream, void if (ret < 0) break; } - ret = __snd_pcm_lib_xfer(substream, bufs, false, frames, - in_kernel); + ret = snd_pcm_kernel_writev(substream, bufs, frames); if (ret != -EPIPE && ret != -ESTRPIPE) break;
@@ -1262,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; @@ -1283,13 +1283,13 @@ snd_pcm_sframes_t snd_pcm_oss_readv3(struct snd_pcm_substream *substream, void * if (ret < 0) break; } - ret = __snd_pcm_lib_xfer(substream, bufs, false, frames, - in_kernel); + 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) { diff --git a/sound/core/oss/pcm_plugin.h b/sound/core/oss/pcm_plugin.h index 73c068abaca5..c9cd29d86efd 100644 --- a/sound/core/oss/pcm_plugin.h +++ b/sound/core/oss/pcm_plugin.h @@ -162,11 +162,9 @@ 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
Signed-off-by: Takashi Iwai tiwai@suse.de --- .../sound/kernel-api/writing-an-alsa-driver.rst | 111 ++++++++++++++------- 1 file changed, 76 insertions(+), 35 deletions(-)
diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst index 95c5443eff38..58ffa3f5bda7 100644 --- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst +++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -2080,8 +2080,8 @@ sleeping poll threads, etc.
This callback is also atomic as default.
-copy and silence callbacks -~~~~~~~~~~~~~~~~~~~~~~~~~~ +copy_user, copy_kernel and fill_silence ops +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These callbacks are not mandatory, and can be omitted in most cases. These callbacks are used when the hardware buffer cannot be in the @@ -3532,8 +3532,9 @@ external hardware buffer in interrupts (or in tasklets, preferably).
The first case works fine if the external hardware buffer is large enough. This method doesn't need any extra buffers and thus is more -effective. You need to define the ``copy`` and ``silence`` callbacks -for the data transfer. However, there is a drawback: it cannot be +effective. You need to define the ``copy_user`` and ``copy_kernel`` +callbacks for the data transfer, in addition to ``fill_silence`` +callback for playback. However, there is a drawback: it cannot be mmapped. The examples are GUS's GF1 PCM or emu8000's wavetable PCM.
The second case allows for mmap on the buffer, although you have to @@ -3545,30 +3546,34 @@ Another case is when the chip uses a PCI memory-map region for the buffer instead of the host memory. In this case, mmap is available only on certain architectures like the Intel one. In non-mmap mode, the data cannot be transferred as in the normal way. Thus you need to define the -``copy`` and ``silence`` callbacks as well, as in the cases above. The -examples are found in ``rme32.c`` and ``rme96.c``. +``copy_user``, ``copy_kernel`` and ``fill_silence`` callbacks as well, +as in the cases above. The examples are found in ``rme32.c`` and +``rme96.c``.
-The implementation of the ``copy`` and ``silence`` callbacks depends -upon whether the hardware supports interleaved or non-interleaved -samples. The ``copy`` callback is defined like below, a bit -differently depending whether the direction is playback or capture: +The implementation of the ``copy_user``, ``copy_kernel`` and +``silence`` callbacks depends upon whether the hardware supports +interleaved or non-interleaved samples. The ``copy_user`` callback is +defined like below, a bit differently depending whether the direction +is playback or capture:
::
- static int playback_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count); - static int capture_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count); + static int playback_copy_user(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *src, unsigned long count); + static int capture_copy_user(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void __user *dst, unsigned long count);
In the case of interleaved samples, the second argument (``channel``) is not used. The third argument (``pos``) points the current position -offset in frames. +offset in bytes.
The meaning of the fourth argument is different between playback and capture. For playback, it holds the source data pointer, and for capture, it's the destination data pointer.
-The last argument is the number of frames to be copied. +The last argument is the number of bytes to be copied.
What you have to do in this callback is again different between playback and capture directions. In the playback case, you copy the given amount @@ -3578,8 +3583,7 @@ way, the copy would be like:
::
- my_memcpy(my_buffer + frames_to_bytes(runtime, pos), src, - frames_to_bytes(runtime, count)); + my_memcpy_from_user(my_buffer + pos, src, count);
For the capture direction, you copy the given amount of data (``count``) at the specified offset (``pos``) on the hardware buffer to the @@ -3587,31 +3591,68 @@ specified pointer (``dst``).
::
- my_memcpy(dst, my_buffer + frames_to_bytes(runtime, pos), - frames_to_bytes(runtime, count)); + my_memcpy_to_user(dst, my_buffer + pos, count); + +Here the functions are named as ``from_user`` and ``to_user`` because +it's the user-space buffer that is passed to these callbacks. That +is, the callback is supposed to copy from/to the user-space data +directly to/from the hardware buffer.
-Note that both the position and the amount of data are given in frames. +Careful readers might notice that these callbacks receive the +arguments in bytes, not in frames like other callbacks. It's because +it would make coding easier like the examples above, and also it makes +easier to unify both the interleaved and non-interleaved cases, as +explained in the following.
In the case of non-interleaved samples, the implementation will be a bit -more complicated. +more complicated. The callback is called for each channel, passed by +the second argument, so totally it's called for N-channels times per +transfer. + +The meaning of other arguments are almost same as the interleaved +case. The callback is supposed to copy the data from/to the given +user-space buffer, but only for the given channel. For the detailed +implementations, please check ``isa/gus/gus_pcm.c`` or +"pci/rme9652/rme9652.c" as examples. + +The above callbacks are the copy from/to the user-space buffer. There +are some cases where we want copy from/to the kernel-space buffer +instead. In such a case, ``copy_kernel`` callback is called. It'd +look like: + +:: + + static int playback_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *src, unsigned long count); + static int capture_copy_kernel(struct snd_pcm_substream *substream, + int channel, unsigned long pos, + void *dst, unsigned long count); + +As found easily, the only difference is that the buffer pointer is +without ``__user`` prefix; that is, a kernel-buffer pointer is passed +in the fourth argument. Correspondingly, the implementation would be +a version without the user-copy, such as:
-You need to check the channel argument, and if it's -1, copy the whole -channels. Otherwise, you have to copy only the specified channel. Please -check ``isa/gus/gus_pcm.c`` as an example. +:: + + my_memcpy(my_buffer + pos, src, count);
-The ``silence`` callback is also implemented in a similar way +Usually for the playback, another callback ``fill_silence`` is +defined. It's implemented in a similar way as the copy callbacks +above:
::
static int silence(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, snd_pcm_uframes_t count); + unsigned long pos, unsigned long count);
-The meanings of arguments are the same as in the ``copy`` callback, -although there is no ``src/dst`` argument. In the case of interleaved -samples, the channel argument has no meaning, as well as on ``copy`` -callback. +The meanings of arguments are the same as in the ``copy_user`` and +``copy_kernel`` callbacks, although there is no buffer pointer +argument. In the case of interleaved samples, the channel argument has +no meaning, as well as on ``copy_*`` callbacks.
-The role of ``silence`` callback is to set the given amount +The role of ``fill_silence`` callback is to set the given amount (``count``) of silence data at the specified offset (``pos``) on the hardware buffer. Suppose that the data format is signed (that is, the silent-data is 0), and the implementation using a memset-like function @@ -3619,11 +3660,11 @@ would be like:
::
- my_memcpy(my_buffer + frames_to_bytes(runtime, pos), 0, - frames_to_bytes(runtime, count)); + my_memset(my_buffer + pos, 0, count);
In the case of non-interleaved samples, again, the implementation -becomes a bit more complicated. See, for example, ``isa/gus/gus_pcm.c``. +becomes a bit more complicated, as it's called N-times per transfer +for each channel. See, for example, ``isa/gus/gus_pcm.c``.
Non-Contiguous Buffers ----------------------
On Jul 02 2017 05:58, Takashi Iwai wrote:
Hi,
this is a full patchset of what I sent previously, containing the all changes instead of the snippet. The main purpose of this patchset is to eliminate the remaining usages of set_fs(). They are basically used for in-kernel PCM data transfer, and this patch provides the new API functions and replaces the hackish set_fs() calls with them.
Unlike the first patchset with the unified copy_silence ops, this adds a new copy_kernel ops instead. At the same time, copy/silence are changed to receive the position and size in bytes instead of frames. This allows us to simplify the PCM core code. As a result, a good amount of code could be removed from pcm_lib.c.
The difference from the previous patchset is that this is a full patchset, i.e. all relevant drivers have been covered, and also some small issues have been addressed, in addition, the documentation update is provided, too.
I'm Cc'ing the media and the USB people since it touches solo6x10 and usb-gadget drivers.
The previous ACK was dropped as each patch was rewritten again. Sorry for the doubly patch-review labours.
thanks,
Takashi
===
Takashi Iwai (26): ALSA: pcm: Introduce copy_user, copy_kernel and fill_silence ops
Below commits look good to me.
ALSA: dummy: Convert to new PCM copy ops ALSA: es1938: Convert to the new PCM copy ops ALSA: nm256: Convert to new PCM copy ops ALSA: korg1212: Convert to the new PCM ops ALSA: rme32: Convert to the new PCM copy ops ALSA: rme96: Convert to the new PCM ops ALSA: rme9652: Convert to the new PCM ops ALSA: hdsp: Convert to the new PCM ops ALSA: gus: Convert to the new PCM ops ALSA: sb: Convert to the new PCM ops ALSA: sh: Convert to the new PCM ops ASoC: blackfin: Convert to the new PCM ops [media] solo6x10: Convert to the new PCM ops ALSA: pcm: Drop the old copy and silence ops ALSA: pcm: Check PCM state by a common helper function ALSA: pcm: Shuffle codes ALSA: pcm: Call directly the common read/write helpers ALSA: pcm: More unification of PCM transfer codes ALSA: pcm: Unify read/write loop ALSA: pcm: Simplify snd_pcm_playback_silence() ALSA: pcm: Direct in-kernel read/write support usb: gadget: u_uac1: Kill set_fs() usage ALSA: pcm: Kill set_fs() in PCM OSS layer ALSA: pcm: Build OSS writev/readv helpers conditionally ALSA: doc: Update copy_user, copy_kernel and fill_silence PCM ops
Reviewed-by: Takashi Sakamoto o-takashi@sakamocchi.jp
I did easy test with snd-hda-intel/snd-fireworks in below conditions. Things work well: 1. ALSA application (aplay/arecord) for '__user *' <-> '__kernel *' copying. 2. loaded snd-oss-pcm and an Open Sound System application (ossplay/ossrecord), for '__kernel *' <-> '__kernel *' copying.
I have no devices for which drivers have the .copy_user, .copy_kernel and .fill_silence, and all of my attemps to work with OTG chip for v4.12 fails (sigh...). My test is not comprehensive at all, however the patchset is programmed with handler-oriented ways and in this point I think snd-pcm works as expected.
I note that patch 19 brings merge conflict to current HEAD ee6f4cde4f74("Merge branch 'for-linus'"), due to my patch, 2c4842d3b6b3("ALSA: pcm: add local header file for snd-pcm module"). I should have postponed it.. For the above test, I handy modifies the history with little affections for my reviewing.
.../sound/kernel-api/writing-an-alsa-driver.rst | 111 ++-- drivers/media/pci/solo6x10/solo6x10-g723.c | 32 +- drivers/usb/gadget/function/u_uac1.c | 7 +- include/sound/pcm.h | 80 ++- sound/core/oss/io.c | 4 +- sound/core/oss/pcm_oss.c | 81 +-- sound/core/oss/pcm_plugin.h | 6 +- sound/core/pcm_lib.c | 564 ++++++++------------- sound/drivers/dummy.c | 20 +- sound/isa/gus/gus_pcm.c | 97 ++-- sound/isa/sb/emu8000_pcm.c | 190 ++++--- sound/pci/es1938.c | 33 +- sound/pci/korg1212/korg1212.c | 112 ++-- sound/pci/nm256/nm256.c | 57 ++- sound/pci/rme32.c | 65 ++- sound/pci/rme96.c | 70 ++- sound/pci/rme9652/hdsp.c | 67 ++- sound/pci/rme9652/rme9652.c | 71 ++- sound/sh/sh_dac_audio.c | 54 +- sound/soc/blackfin/bf5xx-ac97-pcm.c | 27 +- sound/soc/blackfin/bf5xx-i2s-pcm.c | 36 +- sound/soc/soc-pcm.c | 5 +- 22 files changed, 977 insertions(+), 812 deletions(-)
Regards
Takashi Sakamoto
participants (6)
-
Greg Kroah-Hartman
-
Hans Verkuil
-
Mark Brown
-
Sergei Shtylyov
-
Takashi Iwai
-
Takashi Sakamoto