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