[alsa-devel] [PATCH 22/35] axfer: add support for non-blocking operation

Takashi Sakamoto o-takashi at sakamocchi.jp
Tue Nov 13 07:41:34 CET 2018


In alsa-lib PCM API, snd_pcm_read[i|n]() and snd_pcm_write[i|n] can be
used with non-blocking mode. This is available when SND_PCM_NONBLOCK is
used as 'mode' argument for a call of snd_pcm_open().

This commit adds support this type of operation. To reduce CPU usage, this
commit uses 'snd_pcm_wait()' to wait for event notification.

Below lines are examples to execute:
$ axfer transfer -N -P -d 2 -D hw:0,3 /dev/urandom -f dat -vvv
$ axfer transfer -N -C -d 2 -D hw:1,0 /dev/null -r 48000 -vvv

Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
 axfer/xfer-libasound-irq-rw.c | 102 ++++++++++++++++++++++++++++++++--
 axfer/xfer-libasound.c        |  16 +++++-
 axfer/xfer-libasound.h        |   1 +
 3 files changed, 113 insertions(+), 6 deletions(-)

diff --git a/axfer/xfer-libasound-irq-rw.c b/axfer/xfer-libasound-irq-rw.c
index 59634b4..f05ac4b 100644
--- a/axfer/xfer-libasound-irq-rw.c
+++ b/axfer/xfer-libasound-irq-rw.c
@@ -117,6 +117,51 @@ error:
 	return err;
 }
 
+static int r_process_frames_nonblocking(struct libasound_state *state,
+					snd_pcm_state_t status,
+					unsigned int *frame_count,
+					struct mapper_context *mapper,
+					struct container_context *cntrs)
+{
+	snd_pcm_sframes_t avail;
+	snd_pcm_uframes_t avail_count;
+	int err = 0;
+
+	if (status != SND_PCM_STATE_RUNNING) {
+		err = snd_pcm_start(state->handle);
+		if (err < 0)
+			goto error;
+	}
+
+	// Wait for hardware IRQ when no available space.
+	err = snd_pcm_wait(state->handle, -1);
+	if (err < 0)
+		goto error;
+
+	// Check available space on the buffer.
+	avail = snd_pcm_avail(state->handle);
+	if (avail < 0) {
+		err = avail;
+		goto error;
+	}
+	avail_count = (snd_pcm_uframes_t)avail;
+
+	if (avail_count == 0) {
+		// Let's go to a next iteration.
+		err = 0;
+		goto error;
+	}
+
+	err = read_frames(state, frame_count, avail_count, mapper, cntrs);
+	if (err < 0)
+		goto error;
+
+	return 0;
+error:
+	*frame_count = 0;
+	return err;
+}
+
 static int write_frames(struct libasound_state *state,
 			unsigned int *frame_count, unsigned int avail_count,
 			struct mapper_context *mapper,
@@ -231,6 +276,48 @@ error:
 	return err;
 }
 
+static int w_process_frames_nonblocking(struct libasound_state *state,
+					snd_pcm_state_t status,
+					unsigned int *frame_count,
+					struct mapper_context *mapper,
+					struct container_context *cntrs)
+{
+	snd_pcm_sframes_t avail;
+	unsigned int avail_count;
+	int err;
+
+	// Wait for hardware IRQ when no left space.
+	err = snd_pcm_wait(state->handle, -1);
+	if (err < 0)
+		goto error;
+
+	// Check available space on the buffer.
+	avail = snd_pcm_avail(state->handle);
+	if (avail < 0) {
+		err = avail;
+		goto error;
+	}
+	avail_count = (unsigned int)avail;
+
+	if (avail_count == 0) {
+		// Let's go to a next iteration.
+		err = 0;
+		goto error;
+	}
+
+	err = write_frames(state, frame_count, avail_count, mapper, cntrs);
+	if (err < 0)
+		goto error;
+
+	// NOTE: The substream starts automatically when the accumulated number
+	// of queued data frame exceeds start_threshold.
+
+	return 0;
+error:
+	*frame_count = 0;
+	return err;
+}
+
 static int irq_rw_pre_process(struct libasound_state *state)
 {
 	struct rw_closure *closure = state->private_data;
@@ -267,10 +354,17 @@ static int irq_rw_pre_process(struct libasound_state *state)
 	if (err < 0)
 		return err;
 
-	if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE)
-		closure->process_frames = r_process_frames_blocking;
-	else
-		closure->process_frames = w_process_frames_blocking;
+	if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE) {
+		if (state->nonblock)
+			closure->process_frames = r_process_frames_nonblocking;
+		else
+			closure->process_frames = r_process_frames_blocking;
+	} else {
+		if (state->nonblock)
+			closure->process_frames = w_process_frames_nonblocking;
+		else
+			closure->process_frames = w_process_frames_blocking;
+	}
 
 	return 0;
 }
diff --git a/axfer/xfer-libasound.c b/axfer/xfer-libasound.c
index 77c142e..cb26b69 100644
--- a/axfer/xfer-libasound.c
+++ b/axfer/xfer-libasound.c
@@ -14,9 +14,10 @@ enum no_short_opts {
 	OPT_FATAL_ERRORS = 200,
 };
 
-#define S_OPTS	"D:"
+#define S_OPTS	"D:N"
 static const struct option l_opts[] = {
 	{"device",		1, 0, 'D'},
+	{"nonblock",		0, 0, 'N'},
 	// For debugging.
 	{"fatal-errors",	0, 0, OPT_FATAL_ERRORS},
 };
@@ -46,6 +47,8 @@ static int xfer_libasound_parse_opt(struct xfer_context *xfer, int key,
 
 	if (key == 'D')
 		state->node_literal = arg_duplicate_string(optarg, &err);
+	else if (key == 'N')
+		state->nonblock = true;
 	else if (key == OPT_FATAL_ERRORS)
 		state->finish_at_xrun = true;
 	else
@@ -91,10 +94,14 @@ static int set_access_hw_param(struct libasound_state *state)
 static int open_handle(struct xfer_context *xfer)
 {
 	struct libasound_state *state = xfer->private_data;
+	int mode = 0;
 	int err;
 
+	if (state->nonblock)
+		mode |= SND_PCM_NONBLOCK;
+
 	err = snd_pcm_open(&state->handle, state->node_literal, xfer->direction,
-			   0);
+			   mode);
 	if (err < 0) {
 		logging(state, "Fail to open libasound PCM node for %s: %s\n",
 			snd_pcm_stream_name(xfer->direction),
@@ -378,7 +385,12 @@ static void xfer_libasound_post_process(struct xfer_context *xfer)
 				logging(state, "snd_pcm_drop(): %s\n",
 				       snd_strerror(err));
 		} else {
+			// TODO: this is a bug in kernel land.
+			if (state->nonblock)
+				snd_pcm_nonblock(state->handle, 0);
 			err = snd_pcm_drain(state->handle);
+			if (state->nonblock)
+				snd_pcm_nonblock(state->handle, 1);
 			if (err < 0)
 				logging(state, "snd_pcm_drain(): %s\n",
 				       snd_strerror(err));
diff --git a/axfer/xfer-libasound.h b/axfer/xfer-libasound.h
index 270288d..6656aeb 100644
--- a/axfer/xfer-libasound.h
+++ b/axfer/xfer-libasound.h
@@ -31,6 +31,7 @@ struct libasound_state {
 	char *node_literal;
 
 	bool finish_at_xrun:1;
+	bool nonblock:1;
 };
 
 // For internal use in 'libasound' module.
-- 
2.19.1



More information about the Alsa-devel mailing list