1/* 2 * Helper functions for indirect PCM data transfer to a simple FIFO in 3 * hardware (small, no possibility to read "hardware io position", 4 * updating position done by interrupt, ...) 5 * 6 * Copyright (c) by 2007 Joachim Foerster <JOFT@gmx.de> 7 * 8 * Based on "pcm-indirect.h" (alsa-driver-1.0.13) by 9 * 10 * Copyright (c) by Takashi Iwai <tiwai@suse.de> 11 * Jaroslav Kysela <perex@suse.cz> 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, write to the Free Software 25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 26 */ 27 28/* snd_printk/d() */ 29#include <sound/core.h> 30/* struct snd_pcm_substream, struct snd_pcm_runtime, snd_pcm_uframes_t 31 * snd_pcm_period_elapsed() */ 32#include <sound/pcm.h> 33 34#include "pcm-indirect2.h" 35 36#ifdef SND_PCM_INDIRECT2_STAT 37/* jiffies */ 38#include <linux/jiffies.h> 39 40void snd_pcm_indirect2_stat(struct snd_pcm_substream *substream, 41 struct snd_pcm_indirect2 *rec) 42{ 43 struct snd_pcm_runtime *runtime = substream->runtime; 44 int i; 45 int j; 46 int k; 47 int seconds = (rec->lastbytetime - rec->firstbytetime) / HZ; 48 49 snd_printk(KERN_DEBUG "STAT: mul_elapsed: %u, mul_elapsed_real: %d, " 50 "irq_occured: %d\n", 51 rec->mul_elapsed, rec->mul_elapsed_real, rec->irq_occured); 52 snd_printk(KERN_DEBUG "STAT: min_multiple: %d (irqs/period)\n", 53 rec->min_multiple); 54 snd_printk(KERN_DEBUG "STAT: firstbytetime: %lu, lastbytetime: %lu, " 55 "firstzerotime: %lu\n", 56 rec->firstbytetime, rec->lastbytetime, rec->firstzerotime); 57 snd_printk(KERN_DEBUG "STAT: bytes2hw: %u Bytes => (by runtime->rate) " 58 "length: %d s\n", 59 rec->bytes2hw, rec->bytes2hw / 2 / 2 / runtime->rate); 60 snd_printk(KERN_DEBUG "STAT: (by measurement) length: %d => " 61 "rate: %d Bytes/s = %d Frames/s|Hz\n", 62 seconds, rec->bytes2hw / seconds, 63 rec->bytes2hw / 2 / 2 / seconds); 64 snd_printk(KERN_DEBUG 65 "STAT: zeros2hw: %u = %d ms ~ %d * %d zero copies\n", 66 rec->zeros2hw, ((rec->zeros2hw / 2 / 2) * 1000) / 67 runtime->rate, 68 rec->zeros2hw / (rec->hw_buffer_size / 2), 69 (rec->hw_buffer_size / 2)); 70 snd_printk(KERN_DEBUG "STAT: pointer_calls: %u, lastdifftime: %u\n", 71 rec->pointer_calls, rec->lastdifftime); 72 snd_printk(KERN_DEBUG "STAT: sw_io: %d, sw_data: %d\n", rec->sw_io, 73 rec->sw_data); 74 snd_printk(KERN_DEBUG "STAT: byte_sizes[]:\n"); 75 k = 0; 76 for (j = 0; j < 8; j++) { 77 for (i = j * 8; i < (j + 1) * 8; i++) 78 if (rec->byte_sizes[i] != 0) { 79 snd_printk(KERN_DEBUG "%u: %u", 80 i, rec->byte_sizes[i]); 81 k++; 82 } 83 if (((k % 8) == 0) && (k != 0)) { 84 snd_printk(KERN_DEBUG "\n"); 85 k = 0; 86 } 87 } 88 snd_printk(KERN_DEBUG "\n"); 89 snd_printk(KERN_DEBUG "STAT: zero_sizes[]:\n"); 90 for (j = 0; j < 8; j++) { 91 k = 0; 92 for (i = j * 8; i < (j + 1) * 8; i++) 93 if (rec->zero_sizes[i] != 0) 94 snd_printk(KERN_DEBUG "%u: %u", 95 i, rec->zero_sizes[i]); 96 else 97 k++; 98 if (!k) 99 snd_printk(KERN_DEBUG "\n"); 100 } 101 snd_printk(KERN_DEBUG "\n"); 102 snd_printk(KERN_DEBUG "STAT: min_adds[]:\n"); 103 for (j = 0; j < 8; j++) { 104 if (rec->min_adds[j] != 0) 105 snd_printk(KERN_DEBUG "%u: %u", j, rec->min_adds[j]); 106 } 107 snd_printk(KERN_DEBUG "\n"); 108 snd_printk(KERN_DEBUG "STAT: mul_adds[]:\n"); 109 for (j = 0; j < 8; j++) { 110 if (rec->mul_adds[j] != 0) 111 snd_printk(KERN_DEBUG "%u: %u", j, rec->mul_adds[j]); 112 } 113 snd_printk(KERN_DEBUG "\n"); 114 snd_printk(KERN_DEBUG 115 "STAT: zero_times_saved: %d, zero_times_notsaved: %d\n", 116 rec->zero_times_saved, rec->zero_times_notsaved); 117 /* snd_printk(KERN_DEBUG "STAT: zero_times[]\n"); 118 i = 0; 119 for (j = 0; j < 3750; j++) { 120 if (rec->zero_times[j] != 0) { 121 snd_printk(KERN_DEBUG "%u: %u", j, rec->zero_times[j]); 122 i++; 123 } 124 if (((i % 8) == 0) && (i != 0)) 125 snd_printk(KERN_DEBUG "\n"); 126 } 127 snd_printk(KERN_DEBUG "\n"); */ 128 return; 129} 130#endif 131 132/* 133 * _internal_ helper function for playback/capture transfer function 134 */ 135static void 136snd_pcm_indirect2_increase_min_periods(struct snd_pcm_substream *substream, 137 struct snd_pcm_indirect2 *rec, 138 int isplay, int iscopy, 139 unsigned int bytes) 140{ 141 if (rec->min_periods >= 0) { 142 if (iscopy) { 143 rec->sw_io += bytes; 144 if (rec->sw_io >= rec->sw_buffer_size) 145 rec->sw_io -= rec->sw_buffer_size; 146 } else if (isplay) { 147 /* If application does not write data in multiples of 148 * a period, move sw_data to the next correctly aligned 149 * position, so that sw_io can converge to it (in the 150 * next step). 151 */ 152 if (!rec->check_alignment) { 153 if (rec->bytes2hw % 154 snd_pcm_lib_period_bytes(substream)) { 155 unsigned bytes2hw_aligned = 156 (1 + 157 (rec->bytes2hw / 158 snd_pcm_lib_period_bytes 159 (substream))) * 160 snd_pcm_lib_period_bytes 161 (substream); 162 rec->sw_data = 163 bytes2hw_aligned % 164 rec->sw_buffer_size; 165#ifdef SND_PCM_INDIRECT2_STAT 166 snd_printk(KERN_DEBUG 167 "STAT: @re-align: aligned " 168 "bytes2hw to next period " 169 "size boundary: %d " 170 "(instead of %d)\n", 171 bytes2hw_aligned, 172 rec->bytes2hw); 173 snd_printk(KERN_DEBUG 174 "STAT: @re-align: sw_data " 175 "moves to: %d\n", 176 rec->sw_data); 177#endif 178 } 179 rec->check_alignment = 1; 180 } 181 /* We are at the end and are copying zeros into the 182 * fifo. 183 * Now, we have to make sure that sw_io is increased 184 * until the position of sw_data: Filling the fifo with 185 * the first zeros means, the last bytes were played. 186 */ 187 if (rec->sw_io != rec->sw_data) { 188 unsigned int diff; 189 if (rec->sw_data > rec->sw_io) 190 diff = rec->sw_data - rec->sw_io; 191 else 192 diff = (rec->sw_buffer_size - 193 rec->sw_io) + 194 rec->sw_data; 195 if (bytes >= diff) 196 rec->sw_io = rec->sw_data; 197 else { 198 rec->sw_io += bytes; 199 if (rec->sw_io >= rec->sw_buffer_size) 200 rec->sw_io -= 201 rec->sw_buffer_size; 202 } 203 } 204 } 205 rec->min_period_count += bytes; 206 if (rec->min_period_count >= (rec->hw_buffer_size / 2)) { 207 rec->min_periods += (rec->min_period_count / 208 (rec->hw_buffer_size / 2)); 209#ifdef SND_PCM_INDIRECT2_STAT 210 if ((rec->min_period_count / 211 (rec->hw_buffer_size / 2)) > 7) 212 snd_printk(KERN_DEBUG 213 "STAT: more than 7 (%d) min_adds " 214 "at once - too big to save!\n", 215 (rec->min_period_count / 216 (rec->hw_buffer_size / 2))); 217 else 218 rec->min_adds[(rec->min_period_count / 219 (rec->hw_buffer_size / 2))]++; 220#endif 221 rec->min_period_count = (rec->min_period_count % 222 (rec->hw_buffer_size / 2)); 223 } 224 } else if (isplay && iscopy) 225 rec->min_periods = 0; 226} 227 228/* 229 * helper function for playback/capture pointer callback 230 */ 231snd_pcm_uframes_t 232snd_pcm_indirect2_pointer(struct snd_pcm_substream *substream, 233 struct snd_pcm_indirect2 *rec) 234{ 235#ifdef SND_PCM_INDIRECT2_STAT 236 rec->pointer_calls++; 237#endif 238 return bytes_to_frames(substream->runtime, rec->sw_io); 239} 240 241/* 242 * _internal_ helper function for playback interrupt callback 243 */ 244static void 245snd_pcm_indirect2_playback_transfer(struct snd_pcm_substream *substream, 246 struct snd_pcm_indirect2 *rec, 247 snd_pcm_indirect2_copy_t copy, 248 snd_pcm_indirect2_zero_t zero) 249{ 250 struct snd_pcm_runtime *runtime = substream->runtime; 251 snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; 252 253 /* runtime->control->appl_ptr: position where ALSA will write next time 254 * rec->appl_ptr: position where ALSA was last time 255 * diff: obviously ALSA wrote that much bytes into the intermediate 256 * buffer since we checked last time 257 */ 258 snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; 259 260 if (diff) { 261#ifdef SND_PCM_INDIRECT2_STAT 262 rec->lastdifftime = jiffies; 263#endif 264 if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) 265 diff += runtime->boundary; 266 /* number of bytes "added" by ALSA increases the number of 267 * bytes which are ready to "be transferred to HW"/"played" 268 * Then, set rec->appl_ptr to not count bytes twice next time. 269 */ 270 rec->sw_ready += (int)frames_to_bytes(runtime, diff); 271 rec->appl_ptr = appl_ptr; 272 } 273 if (rec->hw_ready && (rec->sw_ready <= 0)) { 274 unsigned int bytes; 275 276#ifdef SND_PCM_INDIRECT2_STAT 277 if (rec->firstzerotime == 0) { 278 rec->firstzerotime = jiffies; 279 snd_printk(KERN_DEBUG 280 "STAT: @firstzerotime: mul_elapsed: %d, " 281 "min_period_count: %d\n", 282 rec->mul_elapsed, rec->min_period_count); 283 snd_printk(KERN_DEBUG 284 "STAT: @firstzerotime: sw_io: %d, " 285 "sw_data: %d, appl_ptr: %u\n", 286 rec->sw_io, rec->sw_data, 287 (unsigned int)appl_ptr); 288 } 289 if ((jiffies - rec->firstzerotime) < 3750) { 290 rec->zero_times[(jiffies - rec->firstzerotime)]++; 291 rec->zero_times_saved++; 292 } else 293 rec->zero_times_notsaved++; 294#endif 295 bytes = zero(substream, rec); 296 297#ifdef SND_PCM_INDIRECT2_STAT 298 rec->zeros2hw += bytes; 299 if (bytes < 64) 300 rec->zero_sizes[bytes]++; 301 else 302 snd_printk(KERN_DEBUG 303 "STAT: %d zero Bytes copied to hardware at " 304 "once - too big to save!\n", 305 bytes); 306#endif 307 snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 0, 308 bytes); 309 return; 310 } 311 while (rec->hw_ready && (rec->sw_ready > 0)) { 312 /* sw_to_end: max. number of bytes that can be read/take from 313 * the current position (sw_data) in _one_ step 314 */ 315 unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data; 316 317 /* bytes: number of bytes we have available (for reading) */ 318 unsigned int bytes = rec->sw_ready; 319 320 if (sw_to_end < bytes) 321 bytes = sw_to_end; 322 if (!bytes) 323 break; 324 325#ifdef SND_PCM_INDIRECT2_STAT 326 if (rec->firstbytetime == 0) 327 rec->firstbytetime = jiffies; 328 rec->lastbytetime = jiffies; 329#endif 330 /* copy bytes from intermediate buffer position sw_data to the 331 * HW and return number of bytes actually written 332 * Furthermore, set hw_ready to 0, if the fifo isn't empty 333 * now => more could be transferred to fifo 334 */ 335 bytes = copy(substream, rec, bytes); 336 rec->bytes2hw += bytes; 337 338#ifdef SND_PCM_INDIRECT2_STAT 339 if (bytes < 64) 340 rec->byte_sizes[bytes]++; 341 else 342 snd_printk(KERN_DEBUG 343 "STAT: %d Bytes copied to hardware at once " 344 "- too big to save!\n", 345 bytes); 346#endif 347 /* increase sw_data by the number of actually written bytes 348 * (= number of taken bytes from intermediate buffer) 349 */ 350 rec->sw_data += bytes; 351 if (rec->sw_data == rec->sw_buffer_size) 352 rec->sw_data = 0; 353 /* now sw_data is the position where ALSA is going to write 354 * in the intermediate buffer next time = position we are going 355 * to read from next time 356 */ 357 358 snd_pcm_indirect2_increase_min_periods(substream, rec, 1, 1, 359 bytes); 360 361 /* we read bytes from intermediate buffer, so we need to say 362 * that the number of bytes ready for transfer are decreased 363 * now 364 */ 365 rec->sw_ready -= bytes; 366 } 367 return; 368} 369 370/* 371 * helper function for playback interrupt routine 372 */ 373void 374snd_pcm_indirect2_playback_interrupt(struct snd_pcm_substream *substream, 375 struct snd_pcm_indirect2 *rec, 376 snd_pcm_indirect2_copy_t copy, 377 snd_pcm_indirect2_zero_t zero) 378{ 379#ifdef SND_PCM_INDIRECT2_STAT 380 rec->irq_occured++; 381#endif 382 /* hardware played some bytes, so there is room again (in fifo) */ 383 rec->hw_ready = 1; 384 385 /* don't call ack() now, instead call transfer() function directly 386 * (normally called by ack() ) 387 */ 388 snd_pcm_indirect2_playback_transfer(substream, rec, copy, zero); 389 390 if (rec->min_periods >= rec->min_multiple) { 391#ifdef SND_PCM_INDIRECT2_STAT 392 if ((rec->min_periods / rec->min_multiple) > 7) 393 snd_printk(KERN_DEBUG 394 "STAT: more than 7 (%d) mul_adds - too big " 395 "to save!\n", 396 (rec->min_periods / rec->min_multiple)); 397 else 398 rec->mul_adds[(rec->min_periods / 399 rec->min_multiple)]++; 400 rec->mul_elapsed_real += (rec->min_periods / 401 rec->min_multiple); 402 rec->mul_elapsed++; 403#endif 404 rec->min_periods = (rec->min_periods % rec->min_multiple); 405 snd_pcm_period_elapsed(substream); 406 } 407} 408 409/* 410 * _internal_ helper function for capture interrupt callback 411 */ 412static void 413snd_pcm_indirect2_capture_transfer(struct snd_pcm_substream *substream, 414 struct snd_pcm_indirect2 *rec, 415 snd_pcm_indirect2_copy_t copy, 416 snd_pcm_indirect2_zero_t null) 417{ 418 struct snd_pcm_runtime *runtime = substream->runtime; 419 snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; 420 snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; 421 422 if (diff) { 423#ifdef SND_PCM_INDIRECT2_STAT 424 rec->lastdifftime = jiffies; 425#endif 426 if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) 427 diff += runtime->boundary; 428 rec->sw_ready -= frames_to_bytes(runtime, diff); 429 rec->appl_ptr = appl_ptr; 430 } 431 /* if hardware has something, but the intermediate buffer is full 432 * => skip contents of buffer 433 */ 434 if (rec->hw_ready && (rec->sw_ready >= (int)rec->sw_buffer_size)) { 435 unsigned int bytes; 436 437#ifdef SND_PCM_INDIRECT2_STAT 438 if (rec->firstzerotime == 0) { 439 rec->firstzerotime = jiffies; 440 snd_printk(KERN_DEBUG "STAT: (capture) " 441 "@firstzerotime: mul_elapsed: %d, " 442 "min_period_count: %d\n", 443 rec->mul_elapsed, rec->min_period_count); 444 snd_printk(KERN_DEBUG "STAT: (capture) " 445 "@firstzerotime: sw_io: %d, sw_data: %d, " 446 "appl_ptr: %u\n", 447 rec->sw_io, rec->sw_data, 448 (unsigned int)appl_ptr); 449 } 450 if ((jiffies - rec->firstzerotime) < 3750) { 451 rec->zero_times[(jiffies - rec->firstzerotime)]++; 452 rec->zero_times_saved++; 453 } else 454 rec->zero_times_notsaved++; 455#endif 456 bytes = null(substream, rec); 457 458#ifdef SND_PCM_INDIRECT2_STAT 459 rec->zeros2hw += bytes; 460 if (bytes < 64) 461 rec->zero_sizes[bytes]++; 462 else 463 snd_printk(KERN_DEBUG 464 "STAT: (capture) %d zero Bytes copied to " 465 "hardware at once - too big to save!\n", 466 bytes); 467#endif 468 snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 0, 469 bytes); 470 /* report an overrun */ 471 rec->sw_io = SNDRV_PCM_POS_XRUN; 472 return; 473 } 474 while (rec->hw_ready && (rec->sw_ready < (int)rec->sw_buffer_size)) { 475 /* sw_to_end: max. number of bytes that we can write to the 476 * intermediate buffer (until it's end) 477 */ 478 size_t sw_to_end = rec->sw_buffer_size - rec->sw_data; 479 480 /* bytes: max. number of bytes, which may be copied to the 481 * intermediate buffer without overflow (in _one_ step) 482 */ 483 size_t bytes = rec->sw_buffer_size - rec->sw_ready; 484 485 /* limit number of bytes (for transfer) by available room in 486 * the intermediate buffer 487 */ 488 if (sw_to_end < bytes) 489 bytes = sw_to_end; 490 if (!bytes) 491 break; 492 493#ifdef SND_PCM_INDIRECT2_STAT 494 if (rec->firstbytetime == 0) 495 rec->firstbytetime = jiffies; 496 rec->lastbytetime = jiffies; 497#endif 498 /* copy bytes from the intermediate buffer (position sw_data) 499 * to the HW at most and return number of bytes actually copied 500 * from HW 501 * Furthermore, set hw_ready to 0, if the fifo is empty now. 502 */ 503 bytes = copy(substream, rec, bytes); 504 rec->bytes2hw += bytes; 505 506#ifdef SND_PCM_INDIRECT2_STAT 507 if (bytes < 64) 508 rec->byte_sizes[bytes]++; 509 else 510 snd_printk(KERN_DEBUG 511 "STAT: (capture) %d Bytes copied to " 512 "hardware at once - too big to save!\n", 513 bytes); 514#endif 515 /* increase sw_data by the number of actually copied bytes from 516 * HW 517 */ 518 rec->sw_data += bytes; 519 if (rec->sw_data == rec->sw_buffer_size) 520 rec->sw_data = 0; 521 522 snd_pcm_indirect2_increase_min_periods(substream, rec, 0, 1, 523 bytes); 524 525 /* number of bytes in the intermediate buffer, which haven't 526 * been fetched by ALSA yet. 527 */ 528 rec->sw_ready += bytes; 529 } 530 return; 531} 532 533/* 534 * helper function for capture interrupt routine 535 */ 536void 537snd_pcm_indirect2_capture_interrupt(struct snd_pcm_substream *substream, 538 struct snd_pcm_indirect2 *rec, 539 snd_pcm_indirect2_copy_t copy, 540 snd_pcm_indirect2_zero_t null) 541{ 542#ifdef SND_PCM_INDIRECT2_STAT 543 rec->irq_occured++; 544#endif 545 /* hardware recorded some bytes, so there is something to read from the 546 * record fifo: 547 */ 548 rec->hw_ready = 1; 549 550 /* don't call ack() now, instead call transfer() function directly 551 * (normally called by ack() ) 552 */ 553 snd_pcm_indirect2_capture_transfer(substream, rec, copy, null); 554 555 if (rec->min_periods >= rec->min_multiple) { 556 557#ifdef SND_PCM_INDIRECT2_STAT 558 if ((rec->min_periods / rec->min_multiple) > 7) 559 snd_printk(KERN_DEBUG 560 "STAT: more than 7 (%d) mul_adds - " 561 "too big to save!\n", 562 (rec->min_periods / rec->min_multiple)); 563 else 564 rec->mul_adds[(rec->min_periods / 565 rec->min_multiple)]++; 566 rec->mul_elapsed_real += (rec->min_periods / 567 rec->min_multiple); 568 rec->mul_elapsed++; 569#endif 570 rec->min_periods = (rec->min_periods % rec->min_multiple); 571 snd_pcm_period_elapsed(substream); 572 } 573} 574