pcm-indirect.h 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. /*
  2. * Helper functions for indirect PCM data transfer
  3. *
  4. * Copyright (c) by Takashi Iwai <tiwai@suse.de>
  5. * Jaroslav Kysela <perex@suse.cz>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. */
  21. #ifndef __SOUND_PCM_INDIRECT_H
  22. #define __SOUND_PCM_INDIRECT_H
  23. #include <sound/pcm.h>
  24. struct snd_pcm_indirect {
  25. unsigned int hw_buffer_size; /* Byte size of hardware buffer */
  26. unsigned int hw_queue_size; /* Max queue size of hw buffer (0 = buffer size) */
  27. unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */
  28. unsigned int hw_io; /* Ring buffer hw pointer */
  29. int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */
  30. unsigned int sw_buffer_size; /* Byte size of software buffer */
  31. unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */
  32. unsigned int sw_io; /* Current software pointer in bytes */
  33. int sw_ready; /* Bytes ready to be transferred to/from hw */
  34. snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */
  35. };
  36. typedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream,
  37. struct snd_pcm_indirect *rec, size_t bytes);
  38. /*
  39. * helper function for playback ack callback
  40. */
  41. static inline void
  42. snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream,
  43. struct snd_pcm_indirect *rec,
  44. snd_pcm_indirect_copy_t copy)
  45. {
  46. struct snd_pcm_runtime *runtime = substream->runtime;
  47. snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
  48. snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
  49. int qsize;
  50. if (diff) {
  51. if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
  52. diff += runtime->boundary;
  53. rec->sw_ready += (int)frames_to_bytes(runtime, diff);
  54. rec->appl_ptr = appl_ptr;
  55. }
  56. qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
  57. while (rec->hw_ready < qsize && rec->sw_ready > 0) {
  58. unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
  59. unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
  60. unsigned int bytes = qsize - rec->hw_ready;
  61. if (rec->sw_ready < (int)bytes)
  62. bytes = rec->sw_ready;
  63. if (hw_to_end < bytes)
  64. bytes = hw_to_end;
  65. if (sw_to_end < bytes)
  66. bytes = sw_to_end;
  67. if (! bytes)
  68. break;
  69. copy(substream, rec, bytes);
  70. rec->hw_data += bytes;
  71. if (rec->hw_data == rec->hw_buffer_size)
  72. rec->hw_data = 0;
  73. rec->sw_data += bytes;
  74. if (rec->sw_data == rec->sw_buffer_size)
  75. rec->sw_data = 0;
  76. rec->hw_ready += bytes;
  77. rec->sw_ready -= bytes;
  78. }
  79. }
  80. /*
  81. * helper function for playback pointer callback
  82. * ptr = current byte pointer
  83. */
  84. static inline snd_pcm_uframes_t
  85. snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
  86. struct snd_pcm_indirect *rec, unsigned int ptr)
  87. {
  88. int bytes = ptr - rec->hw_io;
  89. if (bytes < 0)
  90. bytes += rec->hw_buffer_size;
  91. rec->hw_io = ptr;
  92. rec->hw_ready -= bytes;
  93. rec->sw_io += bytes;
  94. if (rec->sw_io >= rec->sw_buffer_size)
  95. rec->sw_io -= rec->sw_buffer_size;
  96. if (substream->ops->ack)
  97. substream->ops->ack(substream);
  98. return bytes_to_frames(substream->runtime, rec->sw_io);
  99. }
  100. /*
  101. * helper function for capture ack callback
  102. */
  103. static inline void
  104. snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
  105. struct snd_pcm_indirect *rec,
  106. snd_pcm_indirect_copy_t copy)
  107. {
  108. struct snd_pcm_runtime *runtime = substream->runtime;
  109. snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
  110. snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
  111. if (diff) {
  112. if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
  113. diff += runtime->boundary;
  114. rec->sw_ready -= frames_to_bytes(runtime, diff);
  115. rec->appl_ptr = appl_ptr;
  116. }
  117. while (rec->hw_ready > 0 &&
  118. rec->sw_ready < (int)rec->sw_buffer_size) {
  119. size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
  120. size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
  121. size_t bytes = rec->sw_buffer_size - rec->sw_ready;
  122. if (rec->hw_ready < (int)bytes)
  123. bytes = rec->hw_ready;
  124. if (hw_to_end < bytes)
  125. bytes = hw_to_end;
  126. if (sw_to_end < bytes)
  127. bytes = sw_to_end;
  128. if (! bytes)
  129. break;
  130. copy(substream, rec, bytes);
  131. rec->hw_data += bytes;
  132. if ((int)rec->hw_data == rec->hw_buffer_size)
  133. rec->hw_data = 0;
  134. rec->sw_data += bytes;
  135. if (rec->sw_data == rec->sw_buffer_size)
  136. rec->sw_data = 0;
  137. rec->hw_ready -= bytes;
  138. rec->sw_ready += bytes;
  139. }
  140. }
  141. /*
  142. * helper function for capture pointer callback,
  143. * ptr = current byte pointer
  144. */
  145. static inline snd_pcm_uframes_t
  146. snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
  147. struct snd_pcm_indirect *rec, unsigned int ptr)
  148. {
  149. int qsize;
  150. int bytes = ptr - rec->hw_io;
  151. if (bytes < 0)
  152. bytes += rec->hw_buffer_size;
  153. rec->hw_io = ptr;
  154. rec->hw_ready += bytes;
  155. qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
  156. if (rec->hw_ready > qsize)
  157. return SNDRV_PCM_POS_XRUN;
  158. rec->sw_io += bytes;
  159. if (rec->sw_io >= rec->sw_buffer_size)
  160. rec->sw_io -= rec->sw_buffer_size;
  161. if (substream->ops->ack)
  162. substream->ops->ack(substream);
  163. return bytes_to_frames(substream->runtime, rec->sw_io);
  164. }
  165. #endif /* __SOUND_PCM_INDIRECT_H */