Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c7264cf authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: pcm: Unify read/write loop



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.

Reviewed-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 9f600630
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -1072,10 +1072,7 @@ void snd_pcm_set_sync(struct snd_pcm_substream *substream);
int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
		      unsigned int cmd, void *arg);                      
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);

@@ -1083,28 +1080,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);
}

int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime);
+46 −138
Original line number Diff line number Diff line
@@ -2008,13 +2008,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,
/* 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 __user *buf, unsigned long bytes)
			      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;
}
@@ -2040,6 +2040,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
 */
@@ -2121,7 +2133,8 @@ static int pcm_accessible_state(struct snd_pcm_runtime *runtime)
	}
}

snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
/* 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)
{
@@ -2132,12 +2145,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)
@@ -2150,136 +2165,17 @@ snd_pcm_sframes_t __snd_pcm_lib_write(struct snd_pcm_substream *substream,
	}

	if (!data) {
		if (is_playback)
			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)
		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;

	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;
			transfer = is_playback ?
				default_write_copy : default_read_copy;
	}

	if (size == 0)
		return 0;
@@ -2290,7 +2186,9 @@ snd_pcm_sframes_t __snd_pcm_lib_read(struct snd_pcm_substream *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)
@@ -2300,13 +2198,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);
	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;
			}
@@ -2334,7 +2235,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)
@@ -2353,6 +2254,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;
@@ -2361,7 +2269,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