From patchwork Tue Nov 13 06:41:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Takashi Sakamoto X-Patchwork-Id: 10679937 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7701D4DB2 for ; Tue, 13 Nov 2018 07:37:24 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 647C829911 for ; Tue, 13 Nov 2018 07:37:24 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 596BB2A496; Tue, 13 Nov 2018 07:37:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1CF952A489 for ; Tue, 13 Nov 2018 07:37:23 +0000 (UTC) Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id 05567267B6A; Tue, 13 Nov 2018 07:42:54 +0100 (CET) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 95A66267B86; Tue, 13 Nov 2018 07:42:49 +0100 (CET) Received: from mail-pl1-f196.google.com (mail-pl1-f196.google.com [209.85.214.196]) by alsa0.perex.cz (Postfix) with ESMTP id 13278267AC2 for ; Tue, 13 Nov 2018 07:42:32 +0100 (CET) Received: by mail-pl1-f196.google.com with SMTP id p6-v6so5520812pll.4 for ; Mon, 12 Nov 2018 22:42:32 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=DOn8KewGneCNAuWTAoy1OBX2lNlSJeZKx43Y1VVu+h0=; b=CSmZRids3TdCv/vbCZgCI0nPLNwavlsFlSGm69asiK2K4rWQBgCLTkdJ/rNsN815u4 blZv/k3dZ+QGhHGBn39/9Ys9vbJcF0PuGOUfwMNar2gkdZzeMVrVLlWNfNjgNkTeOV0m mCYSZPEtrgSWsKTn084PZyYu+u5RTeA1wvVdgfUttbME0KVgu63RgJ4T9Wk95f98OcVe 7wQbbDxHCsO3NQ9sbnNYaD/HgCLNHogwn/ycwUd/6EDjEYyGXYd7i3t7UkDgcQ9fCOJP Nw2kRZeS9ajX0bFkWnGCQipEglSPhTtOB7/T3IsF7zzs3ABMNMuaT3CUox8PJUeJs7UK YhgQ== X-Gm-Message-State: AGRZ1gJVd/npTkfdM6dwgW7UlI6EevBomKyzhxpGmDVxl74VmgrK43xM 3PeptiZ+hLOFWPlVtq07Mp6DwnPv X-Google-Smtp-Source: AJdET5fDnsDYcQWo5jxJ541PbLJJN/K+dqlfYNjfTgWQ1eRluR1dbi10hTr475kt029kRi7alR5vKw== X-Received: by 2002:a17:902:b282:: with SMTP id u2mr3930560plr.89.1542091352111; Mon, 12 Nov 2018 22:42:32 -0800 (PST) Received: from localhost.localdomain ([2405:6580:9660:3200:acf1:2274:aafd:ab4c]) by smtp.gmail.com with ESMTPSA id h7sm11207634pfa.105.2018.11.12.22.42.30 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 12 Nov 2018 22:42:31 -0800 (PST) From: Takashi Sakamoto To: tiwai@suse.de, perex@perex.cz Date: Tue, 13 Nov 2018 15:41:34 +0900 Message-Id: <20181113064147.13577-22-o-takashi@sakamocchi.jp> X-Mailer: git-send-email 2.19.1 In-Reply-To: <20181113064147.13577-1-o-takashi@sakamocchi.jp> References: <20181113062459.DD8F7267A5C@alsa0.perex.cz> <20181113064147.13577-1-o-takashi@sakamocchi.jp> MIME-Version: 1.0 Cc: alsa-devel@alsa-project.org Subject: [alsa-devel] [PATCH 22/35] axfer: add support for non-blocking operation X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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.