1c72fcc34Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
2c72fcc34Sopenharmony_ci//
3c72fcc34Sopenharmony_ci// xfer-libasound-irq-rw.c - IRQ-based scheduling model for read/write operation.
4c72fcc34Sopenharmony_ci//
5c72fcc34Sopenharmony_ci// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6c72fcc34Sopenharmony_ci//
7c72fcc34Sopenharmony_ci// Licensed under the terms of the GNU General Public License, version 2.
8c72fcc34Sopenharmony_ci
9c72fcc34Sopenharmony_ci#include "xfer-libasound.h"
10c72fcc34Sopenharmony_ci#include "misc.h"
11c72fcc34Sopenharmony_ci#include "frame-cache.h"
12c72fcc34Sopenharmony_ci
13c72fcc34Sopenharmony_cistruct rw_closure {
14c72fcc34Sopenharmony_ci	snd_pcm_access_t access;
15c72fcc34Sopenharmony_ci	int (*process_frames)(struct libasound_state *state,
16c72fcc34Sopenharmony_ci			      snd_pcm_state_t status, unsigned int *frame_count,
17c72fcc34Sopenharmony_ci			      struct mapper_context *mapper,
18c72fcc34Sopenharmony_ci		      struct container_context *cntrs);
19c72fcc34Sopenharmony_ci	struct frame_cache cache;
20c72fcc34Sopenharmony_ci};
21c72fcc34Sopenharmony_ci
22c72fcc34Sopenharmony_cistatic int wait_for_avail(struct libasound_state *state)
23c72fcc34Sopenharmony_ci{
24c72fcc34Sopenharmony_ci	unsigned int msec_per_buffer;
25c72fcc34Sopenharmony_ci	unsigned short revents;
26c72fcc34Sopenharmony_ci	unsigned short event;
27c72fcc34Sopenharmony_ci	int err;
28c72fcc34Sopenharmony_ci
29c72fcc34Sopenharmony_ci	// Wait during msec equivalent to all audio data frames in buffer
30c72fcc34Sopenharmony_ci	// instead of period, for safe.
31c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_get_buffer_time(state->hw_params,
32c72fcc34Sopenharmony_ci						&msec_per_buffer, NULL);
33c72fcc34Sopenharmony_ci	if (err < 0)
34c72fcc34Sopenharmony_ci		return err;
35c72fcc34Sopenharmony_ci	msec_per_buffer /= 1000;
36c72fcc34Sopenharmony_ci
37c72fcc34Sopenharmony_ci	// Wait for hardware IRQ when no available space.
38c72fcc34Sopenharmony_ci	err = xfer_libasound_wait_event(state, msec_per_buffer, &revents);
39c72fcc34Sopenharmony_ci	if (err < 0)
40c72fcc34Sopenharmony_ci		return err;
41c72fcc34Sopenharmony_ci
42c72fcc34Sopenharmony_ci	// TODO: error reporting.
43c72fcc34Sopenharmony_ci	if (revents & POLLERR)
44c72fcc34Sopenharmony_ci		return -EIO;
45c72fcc34Sopenharmony_ci
46c72fcc34Sopenharmony_ci	if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE)
47c72fcc34Sopenharmony_ci		event = POLLIN;
48c72fcc34Sopenharmony_ci	else
49c72fcc34Sopenharmony_ci		event = POLLOUT;
50c72fcc34Sopenharmony_ci
51c72fcc34Sopenharmony_ci	if (!(revents & event))
52c72fcc34Sopenharmony_ci		return -EAGAIN;
53c72fcc34Sopenharmony_ci
54c72fcc34Sopenharmony_ci	return 0;
55c72fcc34Sopenharmony_ci}
56c72fcc34Sopenharmony_ci
57c72fcc34Sopenharmony_cistatic int read_frames(struct libasound_state *state, unsigned int *frame_count,
58c72fcc34Sopenharmony_ci		       unsigned int avail_count, struct mapper_context *mapper,
59c72fcc34Sopenharmony_ci		       struct container_context *cntrs)
60c72fcc34Sopenharmony_ci{
61c72fcc34Sopenharmony_ci	struct rw_closure *closure = state->private_data;
62c72fcc34Sopenharmony_ci	snd_pcm_sframes_t handled_frame_count;
63c72fcc34Sopenharmony_ci	unsigned int consumed_count;
64c72fcc34Sopenharmony_ci	int err;
65c72fcc34Sopenharmony_ci
66c72fcc34Sopenharmony_ci	// Trim according up to expected frame count.
67c72fcc34Sopenharmony_ci	if (*frame_count < avail_count)
68c72fcc34Sopenharmony_ci		avail_count = *frame_count;
69c72fcc34Sopenharmony_ci
70c72fcc34Sopenharmony_ci	// Cache required amount of frames.
71c72fcc34Sopenharmony_ci	if (avail_count > frame_cache_get_count(&closure->cache)) {
72c72fcc34Sopenharmony_ci		avail_count -= frame_cache_get_count(&closure->cache);
73c72fcc34Sopenharmony_ci
74c72fcc34Sopenharmony_ci		// Execute write operation according to the shape of buffer.
75c72fcc34Sopenharmony_ci		// These operations automatically start the substream.
76c72fcc34Sopenharmony_ci		if (closure->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
77c72fcc34Sopenharmony_ci			handled_frame_count = snd_pcm_readi(state->handle,
78c72fcc34Sopenharmony_ci							closure->cache.buf_ptr,
79c72fcc34Sopenharmony_ci							avail_count);
80c72fcc34Sopenharmony_ci		} else {
81c72fcc34Sopenharmony_ci			handled_frame_count = snd_pcm_readn(state->handle,
82c72fcc34Sopenharmony_ci							closure->cache.buf_ptr,
83c72fcc34Sopenharmony_ci							avail_count);
84c72fcc34Sopenharmony_ci		}
85c72fcc34Sopenharmony_ci		if (handled_frame_count < 0) {
86c72fcc34Sopenharmony_ci			err = handled_frame_count;
87c72fcc34Sopenharmony_ci			return err;
88c72fcc34Sopenharmony_ci		}
89c72fcc34Sopenharmony_ci		frame_cache_increase_count(&closure->cache, handled_frame_count);
90c72fcc34Sopenharmony_ci		avail_count = frame_cache_get_count(&closure->cache);
91c72fcc34Sopenharmony_ci	}
92c72fcc34Sopenharmony_ci
93c72fcc34Sopenharmony_ci	// Write out to file descriptors.
94c72fcc34Sopenharmony_ci	consumed_count = avail_count;
95c72fcc34Sopenharmony_ci	err = mapper_context_process_frames(mapper, closure->cache.buf,
96c72fcc34Sopenharmony_ci					    &consumed_count, cntrs);
97c72fcc34Sopenharmony_ci	if (err < 0)
98c72fcc34Sopenharmony_ci		return err;
99c72fcc34Sopenharmony_ci
100c72fcc34Sopenharmony_ci	frame_cache_reduce(&closure->cache, consumed_count);
101c72fcc34Sopenharmony_ci
102c72fcc34Sopenharmony_ci	*frame_count = consumed_count;
103c72fcc34Sopenharmony_ci
104c72fcc34Sopenharmony_ci	return 0;
105c72fcc34Sopenharmony_ci}
106c72fcc34Sopenharmony_ci
107c72fcc34Sopenharmony_cistatic int r_process_frames_blocking(struct libasound_state *state,
108c72fcc34Sopenharmony_ci				     snd_pcm_state_t status,
109c72fcc34Sopenharmony_ci				     unsigned int *frame_count,
110c72fcc34Sopenharmony_ci				     struct mapper_context *mapper,
111c72fcc34Sopenharmony_ci				     struct container_context *cntrs)
112c72fcc34Sopenharmony_ci{
113c72fcc34Sopenharmony_ci	snd_pcm_sframes_t avail;
114c72fcc34Sopenharmony_ci	snd_pcm_uframes_t avail_count;
115c72fcc34Sopenharmony_ci	int err = 0;
116c72fcc34Sopenharmony_ci
117c72fcc34Sopenharmony_ci	if (status == SND_PCM_STATE_RUNNING) {
118c72fcc34Sopenharmony_ci		// Check available space on the buffer.
119c72fcc34Sopenharmony_ci		avail = snd_pcm_avail(state->handle);
120c72fcc34Sopenharmony_ci		if (avail < 0) {
121c72fcc34Sopenharmony_ci			err = avail;
122c72fcc34Sopenharmony_ci			goto error;
123c72fcc34Sopenharmony_ci		}
124c72fcc34Sopenharmony_ci		avail_count = (snd_pcm_uframes_t)avail;
125c72fcc34Sopenharmony_ci
126c72fcc34Sopenharmony_ci		if (avail_count == 0) {
127c72fcc34Sopenharmony_ci			// Request data frames so that blocking is just
128c72fcc34Sopenharmony_ci			// released.
129c72fcc34Sopenharmony_ci			err = snd_pcm_sw_params_get_avail_min(state->sw_params,
130c72fcc34Sopenharmony_ci							      &avail_count);
131c72fcc34Sopenharmony_ci			if (err < 0)
132c72fcc34Sopenharmony_ci				goto error;
133c72fcc34Sopenharmony_ci		}
134c72fcc34Sopenharmony_ci	} else {
135c72fcc34Sopenharmony_ci		// Request data frames so that the PCM substream starts.
136c72fcc34Sopenharmony_ci		snd_pcm_uframes_t frame_count;
137c72fcc34Sopenharmony_ci		err = snd_pcm_sw_params_get_start_threshold(state->sw_params,
138c72fcc34Sopenharmony_ci							    &frame_count);
139c72fcc34Sopenharmony_ci		if (err < 0)
140c72fcc34Sopenharmony_ci			goto error;
141c72fcc34Sopenharmony_ci
142c72fcc34Sopenharmony_ci		avail_count = (unsigned int)frame_count;
143c72fcc34Sopenharmony_ci	}
144c72fcc34Sopenharmony_ci
145c72fcc34Sopenharmony_ci	err = read_frames(state, frame_count, avail_count, mapper, cntrs);
146c72fcc34Sopenharmony_ci	if (err < 0)
147c72fcc34Sopenharmony_ci		goto error;
148c72fcc34Sopenharmony_ci
149c72fcc34Sopenharmony_ci	return 0;
150c72fcc34Sopenharmony_cierror:
151c72fcc34Sopenharmony_ci	*frame_count = 0;
152c72fcc34Sopenharmony_ci	return err;
153c72fcc34Sopenharmony_ci}
154c72fcc34Sopenharmony_ci
155c72fcc34Sopenharmony_cistatic int r_process_frames_nonblocking(struct libasound_state *state,
156c72fcc34Sopenharmony_ci					snd_pcm_state_t status,
157c72fcc34Sopenharmony_ci					unsigned int *frame_count,
158c72fcc34Sopenharmony_ci					struct mapper_context *mapper,
159c72fcc34Sopenharmony_ci					struct container_context *cntrs)
160c72fcc34Sopenharmony_ci{
161c72fcc34Sopenharmony_ci	snd_pcm_sframes_t avail;
162c72fcc34Sopenharmony_ci	snd_pcm_uframes_t avail_count;
163c72fcc34Sopenharmony_ci	int err = 0;
164c72fcc34Sopenharmony_ci
165c72fcc34Sopenharmony_ci	if (status != SND_PCM_STATE_RUNNING) {
166c72fcc34Sopenharmony_ci		err = snd_pcm_start(state->handle);
167c72fcc34Sopenharmony_ci		if (err < 0)
168c72fcc34Sopenharmony_ci			goto error;
169c72fcc34Sopenharmony_ci	}
170c72fcc34Sopenharmony_ci
171c72fcc34Sopenharmony_ci	if (state->use_waiter) {
172c72fcc34Sopenharmony_ci		err = wait_for_avail(state);
173c72fcc34Sopenharmony_ci		if (err < 0)
174c72fcc34Sopenharmony_ci			goto error;
175c72fcc34Sopenharmony_ci	}
176c72fcc34Sopenharmony_ci
177c72fcc34Sopenharmony_ci	// Check available space on the buffer.
178c72fcc34Sopenharmony_ci	avail = snd_pcm_avail(state->handle);
179c72fcc34Sopenharmony_ci	if (avail < 0) {
180c72fcc34Sopenharmony_ci		err = avail;
181c72fcc34Sopenharmony_ci		goto error;
182c72fcc34Sopenharmony_ci	}
183c72fcc34Sopenharmony_ci	avail_count = (snd_pcm_uframes_t)avail;
184c72fcc34Sopenharmony_ci
185c72fcc34Sopenharmony_ci	if (avail_count == 0) {
186c72fcc34Sopenharmony_ci		// Let's go to a next iteration.
187c72fcc34Sopenharmony_ci		err = 0;
188c72fcc34Sopenharmony_ci		goto error;
189c72fcc34Sopenharmony_ci	}
190c72fcc34Sopenharmony_ci
191c72fcc34Sopenharmony_ci	err = read_frames(state, frame_count, avail_count, mapper, cntrs);
192c72fcc34Sopenharmony_ci	if (err < 0)
193c72fcc34Sopenharmony_ci		goto error;
194c72fcc34Sopenharmony_ci
195c72fcc34Sopenharmony_ci	return 0;
196c72fcc34Sopenharmony_cierror:
197c72fcc34Sopenharmony_ci	*frame_count = 0;
198c72fcc34Sopenharmony_ci	return err;
199c72fcc34Sopenharmony_ci}
200c72fcc34Sopenharmony_ci
201c72fcc34Sopenharmony_cistatic int write_frames(struct libasound_state *state,
202c72fcc34Sopenharmony_ci			unsigned int *frame_count, unsigned int avail_count,
203c72fcc34Sopenharmony_ci			struct mapper_context *mapper,
204c72fcc34Sopenharmony_ci			struct container_context *cntrs)
205c72fcc34Sopenharmony_ci{
206c72fcc34Sopenharmony_ci	struct rw_closure *closure = state->private_data;
207c72fcc34Sopenharmony_ci	snd_pcm_uframes_t consumed_count;
208c72fcc34Sopenharmony_ci	snd_pcm_sframes_t handled_frame_count;
209c72fcc34Sopenharmony_ci	int err;
210c72fcc34Sopenharmony_ci
211c72fcc34Sopenharmony_ci	// Trim according up to expected frame count.
212c72fcc34Sopenharmony_ci	if (*frame_count < avail_count)
213c72fcc34Sopenharmony_ci		avail_count = *frame_count;
214c72fcc34Sopenharmony_ci
215c72fcc34Sopenharmony_ci	// Cache required amount of frames.
216c72fcc34Sopenharmony_ci	if (avail_count > frame_cache_get_count(&closure->cache)) {
217c72fcc34Sopenharmony_ci		avail_count -= frame_cache_get_count(&closure->cache);
218c72fcc34Sopenharmony_ci
219c72fcc34Sopenharmony_ci		// Read frames to transfer.
220c72fcc34Sopenharmony_ci		err = mapper_context_process_frames(mapper,
221c72fcc34Sopenharmony_ci				closure->cache.buf_ptr, &avail_count, cntrs);
222c72fcc34Sopenharmony_ci		if (err < 0)
223c72fcc34Sopenharmony_ci			return err;
224c72fcc34Sopenharmony_ci		frame_cache_increase_count(&closure->cache, avail_count);
225c72fcc34Sopenharmony_ci		avail_count = frame_cache_get_count(&closure->cache);
226c72fcc34Sopenharmony_ci	}
227c72fcc34Sopenharmony_ci
228c72fcc34Sopenharmony_ci	// Execute write operation according to the shape of buffer. These
229c72fcc34Sopenharmony_ci	// operations automatically start the stream.
230c72fcc34Sopenharmony_ci	consumed_count = avail_count;
231c72fcc34Sopenharmony_ci	if (closure->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
232c72fcc34Sopenharmony_ci		handled_frame_count = snd_pcm_writei(state->handle,
233c72fcc34Sopenharmony_ci					closure->cache.buf, consumed_count);
234c72fcc34Sopenharmony_ci	} else {
235c72fcc34Sopenharmony_ci		handled_frame_count = snd_pcm_writen(state->handle,
236c72fcc34Sopenharmony_ci					closure->cache.buf, consumed_count);
237c72fcc34Sopenharmony_ci	}
238c72fcc34Sopenharmony_ci	if (handled_frame_count < 0) {
239c72fcc34Sopenharmony_ci		err = handled_frame_count;
240c72fcc34Sopenharmony_ci		return err;
241c72fcc34Sopenharmony_ci	}
242c72fcc34Sopenharmony_ci
243c72fcc34Sopenharmony_ci	consumed_count = handled_frame_count;
244c72fcc34Sopenharmony_ci	frame_cache_reduce(&closure->cache, consumed_count);
245c72fcc34Sopenharmony_ci
246c72fcc34Sopenharmony_ci	*frame_count = consumed_count;
247c72fcc34Sopenharmony_ci
248c72fcc34Sopenharmony_ci	return 0;
249c72fcc34Sopenharmony_ci}
250c72fcc34Sopenharmony_ci
251c72fcc34Sopenharmony_cistatic int w_process_frames_blocking(struct libasound_state *state,
252c72fcc34Sopenharmony_ci				     snd_pcm_state_t status,
253c72fcc34Sopenharmony_ci				     unsigned int *frame_count,
254c72fcc34Sopenharmony_ci				     struct mapper_context *mapper,
255c72fcc34Sopenharmony_ci				     struct container_context *cntrs)
256c72fcc34Sopenharmony_ci{
257c72fcc34Sopenharmony_ci	snd_pcm_sframes_t avail;
258c72fcc34Sopenharmony_ci	unsigned int avail_count;
259c72fcc34Sopenharmony_ci	int err;
260c72fcc34Sopenharmony_ci
261c72fcc34Sopenharmony_ci	if (status == SND_PCM_STATE_RUNNING) {
262c72fcc34Sopenharmony_ci		// Check available space on the buffer.
263c72fcc34Sopenharmony_ci		avail = snd_pcm_avail(state->handle);
264c72fcc34Sopenharmony_ci		if (avail < 0) {
265c72fcc34Sopenharmony_ci			err = avail;
266c72fcc34Sopenharmony_ci			goto error;
267c72fcc34Sopenharmony_ci		}
268c72fcc34Sopenharmony_ci		avail_count = (unsigned int)avail;
269c72fcc34Sopenharmony_ci
270c72fcc34Sopenharmony_ci		if (avail_count == 0) {
271c72fcc34Sopenharmony_ci			// Fill with data frames so that blocking is just
272c72fcc34Sopenharmony_ci			// released.
273c72fcc34Sopenharmony_ci			snd_pcm_uframes_t avail_min;
274c72fcc34Sopenharmony_ci			err = snd_pcm_sw_params_get_avail_min(state->sw_params,
275c72fcc34Sopenharmony_ci							      &avail_min);
276c72fcc34Sopenharmony_ci			if (err < 0)
277c72fcc34Sopenharmony_ci				goto error;
278c72fcc34Sopenharmony_ci			avail_count = (unsigned int)avail_min;
279c72fcc34Sopenharmony_ci		}
280c72fcc34Sopenharmony_ci	} else {
281c72fcc34Sopenharmony_ci		snd_pcm_uframes_t frames_for_start_threshold;
282c72fcc34Sopenharmony_ci		snd_pcm_uframes_t frames_per_period;
283c72fcc34Sopenharmony_ci
284c72fcc34Sopenharmony_ci		// Fill with data frames so that the PCM substream starts.
285c72fcc34Sopenharmony_ci		err = snd_pcm_sw_params_get_start_threshold(state->sw_params,
286c72fcc34Sopenharmony_ci						&frames_for_start_threshold);
287c72fcc34Sopenharmony_ci		if (err < 0)
288c72fcc34Sopenharmony_ci			goto error;
289c72fcc34Sopenharmony_ci
290c72fcc34Sopenharmony_ci		// But the above number can be too small and cause XRUN because
291c72fcc34Sopenharmony_ci		// I/O operation is done per period.
292c72fcc34Sopenharmony_ci		err = snd_pcm_hw_params_get_period_size(state->hw_params,
293c72fcc34Sopenharmony_ci						&frames_per_period, NULL);
294c72fcc34Sopenharmony_ci		if (err < 0)
295c72fcc34Sopenharmony_ci			goto error;
296c72fcc34Sopenharmony_ci
297c72fcc34Sopenharmony_ci		// Use larger one to prevent from both of XRUN and successive
298c72fcc34Sopenharmony_ci		// blocking.
299c72fcc34Sopenharmony_ci		if (frames_for_start_threshold > frames_per_period)
300c72fcc34Sopenharmony_ci			avail_count = (unsigned int)frames_for_start_threshold;
301c72fcc34Sopenharmony_ci		else
302c72fcc34Sopenharmony_ci			avail_count = (unsigned int)frames_per_period;
303c72fcc34Sopenharmony_ci	}
304c72fcc34Sopenharmony_ci
305c72fcc34Sopenharmony_ci	err = write_frames(state, frame_count, avail_count, mapper, cntrs);
306c72fcc34Sopenharmony_ci	if (err < 0)
307c72fcc34Sopenharmony_ci		goto error;
308c72fcc34Sopenharmony_ci
309c72fcc34Sopenharmony_ci	return 0;
310c72fcc34Sopenharmony_cierror:
311c72fcc34Sopenharmony_ci	*frame_count = 0;
312c72fcc34Sopenharmony_ci	return err;
313c72fcc34Sopenharmony_ci}
314c72fcc34Sopenharmony_ci
315c72fcc34Sopenharmony_cistatic int w_process_frames_nonblocking(struct libasound_state *state,
316c72fcc34Sopenharmony_ci					snd_pcm_state_t pcm_state ATTRIBUTE_UNUSED,
317c72fcc34Sopenharmony_ci					unsigned int *frame_count,
318c72fcc34Sopenharmony_ci					struct mapper_context *mapper,
319c72fcc34Sopenharmony_ci					struct container_context *cntrs)
320c72fcc34Sopenharmony_ci{
321c72fcc34Sopenharmony_ci	snd_pcm_sframes_t avail;
322c72fcc34Sopenharmony_ci	unsigned int avail_count;
323c72fcc34Sopenharmony_ci	int err;
324c72fcc34Sopenharmony_ci
325c72fcc34Sopenharmony_ci	if (state->use_waiter) {
326c72fcc34Sopenharmony_ci		err = wait_for_avail(state);
327c72fcc34Sopenharmony_ci		if (err < 0)
328c72fcc34Sopenharmony_ci			goto error;
329c72fcc34Sopenharmony_ci	}
330c72fcc34Sopenharmony_ci
331c72fcc34Sopenharmony_ci	// Check available space on the buffer.
332c72fcc34Sopenharmony_ci	avail = snd_pcm_avail(state->handle);
333c72fcc34Sopenharmony_ci	if (avail < 0) {
334c72fcc34Sopenharmony_ci		err = avail;
335c72fcc34Sopenharmony_ci		goto error;
336c72fcc34Sopenharmony_ci	}
337c72fcc34Sopenharmony_ci	avail_count = (unsigned int)avail;
338c72fcc34Sopenharmony_ci
339c72fcc34Sopenharmony_ci	if (avail_count == 0) {
340c72fcc34Sopenharmony_ci		// Let's go to a next iteration.
341c72fcc34Sopenharmony_ci		err = 0;
342c72fcc34Sopenharmony_ci		goto error;
343c72fcc34Sopenharmony_ci	}
344c72fcc34Sopenharmony_ci
345c72fcc34Sopenharmony_ci	err = write_frames(state, frame_count, avail_count, mapper, cntrs);
346c72fcc34Sopenharmony_ci	if (err < 0)
347c72fcc34Sopenharmony_ci		goto error;
348c72fcc34Sopenharmony_ci
349c72fcc34Sopenharmony_ci	// NOTE: The substream starts automatically when the accumulated number
350c72fcc34Sopenharmony_ci	// of queued data frame exceeds start_threshold.
351c72fcc34Sopenharmony_ci
352c72fcc34Sopenharmony_ci	return 0;
353c72fcc34Sopenharmony_cierror:
354c72fcc34Sopenharmony_ci	*frame_count = 0;
355c72fcc34Sopenharmony_ci	return err;
356c72fcc34Sopenharmony_ci}
357c72fcc34Sopenharmony_ci
358c72fcc34Sopenharmony_cistatic int irq_rw_pre_process(struct libasound_state *state)
359c72fcc34Sopenharmony_ci{
360c72fcc34Sopenharmony_ci	struct rw_closure *closure = state->private_data;
361c72fcc34Sopenharmony_ci	snd_pcm_format_t format;
362c72fcc34Sopenharmony_ci	snd_pcm_uframes_t frames_per_buffer;
363c72fcc34Sopenharmony_ci	int bytes_per_sample;
364c72fcc34Sopenharmony_ci	unsigned int samples_per_frame;
365c72fcc34Sopenharmony_ci	int err;
366c72fcc34Sopenharmony_ci
367c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_get_format(state->hw_params, &format);
368c72fcc34Sopenharmony_ci	if (err < 0)
369c72fcc34Sopenharmony_ci		return err;
370c72fcc34Sopenharmony_ci	bytes_per_sample = snd_pcm_format_physical_width(format) / 8;
371c72fcc34Sopenharmony_ci	if (bytes_per_sample <= 0)
372c72fcc34Sopenharmony_ci		return -ENXIO;
373c72fcc34Sopenharmony_ci
374c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_get_channels(state->hw_params,
375c72fcc34Sopenharmony_ci					     &samples_per_frame);
376c72fcc34Sopenharmony_ci	if (err < 0)
377c72fcc34Sopenharmony_ci		return err;
378c72fcc34Sopenharmony_ci
379c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_get_buffer_size(state->hw_params,
380c72fcc34Sopenharmony_ci						&frames_per_buffer);
381c72fcc34Sopenharmony_ci	if (err < 0)
382c72fcc34Sopenharmony_ci		return err;
383c72fcc34Sopenharmony_ci
384c72fcc34Sopenharmony_ci	err = snd_pcm_hw_params_get_access(state->hw_params, &closure->access);
385c72fcc34Sopenharmony_ci	if (err < 0)
386c72fcc34Sopenharmony_ci		return err;
387c72fcc34Sopenharmony_ci
388c72fcc34Sopenharmony_ci	err = frame_cache_init(&closure->cache, closure->access,
389c72fcc34Sopenharmony_ci			       bytes_per_sample, samples_per_frame,
390c72fcc34Sopenharmony_ci			       frames_per_buffer);
391c72fcc34Sopenharmony_ci	if (err < 0)
392c72fcc34Sopenharmony_ci		return err;
393c72fcc34Sopenharmony_ci
394c72fcc34Sopenharmony_ci	if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE) {
395c72fcc34Sopenharmony_ci		if (state->nonblock)
396c72fcc34Sopenharmony_ci			closure->process_frames = r_process_frames_nonblocking;
397c72fcc34Sopenharmony_ci		else
398c72fcc34Sopenharmony_ci			closure->process_frames = r_process_frames_blocking;
399c72fcc34Sopenharmony_ci	} else {
400c72fcc34Sopenharmony_ci		if (state->nonblock)
401c72fcc34Sopenharmony_ci			closure->process_frames = w_process_frames_nonblocking;
402c72fcc34Sopenharmony_ci		else
403c72fcc34Sopenharmony_ci			closure->process_frames = w_process_frames_blocking;
404c72fcc34Sopenharmony_ci	}
405c72fcc34Sopenharmony_ci
406c72fcc34Sopenharmony_ci	return 0;
407c72fcc34Sopenharmony_ci}
408c72fcc34Sopenharmony_ci
409c72fcc34Sopenharmony_cistatic int irq_rw_process_frames(struct libasound_state *state,
410c72fcc34Sopenharmony_ci				unsigned int *frame_count,
411c72fcc34Sopenharmony_ci				struct mapper_context *mapper,
412c72fcc34Sopenharmony_ci				struct container_context *cntrs)
413c72fcc34Sopenharmony_ci{
414c72fcc34Sopenharmony_ci	struct rw_closure *closure = state->private_data;
415c72fcc34Sopenharmony_ci	snd_pcm_state_t status;
416c72fcc34Sopenharmony_ci
417c72fcc34Sopenharmony_ci	// Need to recover the stream.
418c72fcc34Sopenharmony_ci	status = snd_pcm_state(state->handle);
419c72fcc34Sopenharmony_ci	if (status != SND_PCM_STATE_RUNNING && status != SND_PCM_STATE_PREPARED)
420c72fcc34Sopenharmony_ci		return -EPIPE;
421c72fcc34Sopenharmony_ci
422c72fcc34Sopenharmony_ci	// NOTE: Actually, status can be shift always.
423c72fcc34Sopenharmony_ci	return closure->process_frames(state, status, frame_count, mapper, cntrs);
424c72fcc34Sopenharmony_ci}
425c72fcc34Sopenharmony_ci
426c72fcc34Sopenharmony_cistatic void irq_rw_post_process(struct libasound_state *state)
427c72fcc34Sopenharmony_ci{
428c72fcc34Sopenharmony_ci	struct rw_closure *closure = state->private_data;
429c72fcc34Sopenharmony_ci
430c72fcc34Sopenharmony_ci	frame_cache_destroy(&closure->cache);
431c72fcc34Sopenharmony_ci}
432c72fcc34Sopenharmony_ci
433c72fcc34Sopenharmony_ciconst struct xfer_libasound_ops xfer_libasound_irq_rw_ops = {
434c72fcc34Sopenharmony_ci	.pre_process	= irq_rw_pre_process,
435c72fcc34Sopenharmony_ci	.process_frames	= irq_rw_process_frames,
436c72fcc34Sopenharmony_ci	.post_process	= irq_rw_post_process,
437c72fcc34Sopenharmony_ci	.private_size	= sizeof(struct rw_closure),
438c72fcc34Sopenharmony_ci};
439