pcm-indirect.h 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /*
  2. * Helper functions for indirect PCM data transfer
  3. *
  4. * Copyright (c) by Takashi Iwai <tiwai@suse.de>
  5. * Jaroslav Kysela <perex@perex.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 int
  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. if (diff < 0)
  54. return -EINVAL;
  55. rec->sw_ready += (int)frames_to_bytes(runtime, diff);
  56. rec->appl_ptr = appl_ptr;
  57. }
  58. qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
  59. while (rec->hw_ready < qsize && rec->sw_ready > 0) {
  60. unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
  61. unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
  62. unsigned int bytes = qsize - rec->hw_ready;
  63. if (rec->sw_ready < (int)bytes)
  64. bytes = rec->sw_ready;
  65. if (hw_to_end < bytes)
  66. bytes = hw_to_end;
  67. if (sw_to_end < bytes)
  68. bytes = sw_to_end;
  69. if (! bytes)
  70. break;
  71. copy(substream, rec, bytes);
  72. rec->hw_data += bytes;
  73. if (rec->hw_data == rec->hw_buffer_size)
  74. rec->hw_data = 0;
  75. rec->sw_data += bytes;
  76. if (rec->sw_data == rec->sw_buffer_size)
  77. rec->sw_data = 0;
  78. rec->hw_ready += bytes;
  79. rec->sw_ready -= bytes;
  80. }
  81. return 0;
  82. }
  83. /*
  84. * helper function for playback pointer callback
  85. * ptr = current byte pointer
  86. */
  87. static inline snd_pcm_uframes_t
  88. snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
  89. struct snd_pcm_indirect *rec, unsigned int ptr)
  90. {
  91. int bytes = ptr - rec->hw_io;
  92. if (bytes < 0)
  93. bytes += rec->hw_buffer_size;
  94. rec->hw_io = ptr;
  95. rec->hw_ready -= bytes;
  96. rec->sw_io += bytes;
  97. if (rec->sw_io >= rec->sw_buffer_size)
  98. rec->sw_io -= rec->sw_buffer_size;
  99. if (substream->ops->ack)
  100. substream->ops->ack(substream);
  101. return bytes_to_frames(substream->runtime, rec->sw_io);
  102. }
  103. /*
  104. * helper function for capture ack callback
  105. */
  106. static inline int
  107. snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
  108. struct snd_pcm_indirect *rec,
  109. snd_pcm_indirect_copy_t copy)
  110. {
  111. struct snd_pcm_runtime *runtime = substream->runtime;
  112. snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
  113. snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
  114. if (diff) {
  115. if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
  116. diff += runtime->boundary;
  117. if (diff < 0)
  118. return -EINVAL;
  119. rec->sw_ready -= frames_to_bytes(runtime, diff);
  120. rec->appl_ptr = appl_ptr;
  121. }
  122. while (rec->hw_ready > 0 &&
  123. rec->sw_ready < (int)rec->sw_buffer_size) {
  124. size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
  125. size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
  126. size_t bytes = rec->sw_buffer_size - rec->sw_ready;
  127. if (rec->hw_ready < (int)bytes)
  128. bytes = rec->hw_ready;
  129. if (hw_to_end < bytes)
  130. bytes = hw_to_end;
  131. if (sw_to_end < bytes)
  132. bytes = sw_to_end;
  133. if (! bytes)
  134. break;
  135. copy(substream, rec, bytes);
  136. rec->hw_data += bytes;
  137. if ((int)rec->hw_data == rec->hw_buffer_size)
  138. rec->hw_data = 0;
  139. rec->sw_data += bytes;
  140. if (rec->sw_data == rec->sw_buffer_size)
  141. rec->sw_data = 0;
  142. rec->hw_ready -= bytes;
  143. rec->sw_ready += bytes;
  144. }
  145. return 0;
  146. }
  147. /*
  148. * helper function for capture pointer callback,
  149. * ptr = current byte pointer
  150. */
  151. static inline snd_pcm_uframes_t
  152. snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
  153. struct snd_pcm_indirect *rec, unsigned int ptr)
  154. {
  155. int qsize;
  156. int bytes = ptr - rec->hw_io;
  157. if (bytes < 0)
  158. bytes += rec->hw_buffer_size;
  159. rec->hw_io = ptr;
  160. rec->hw_ready += bytes;
  161. qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
  162. if (rec->hw_ready > qsize)
  163. return SNDRV_PCM_POS_XRUN;
  164. rec->sw_io += bytes;
  165. if (rec->sw_io >= rec->sw_buffer_size)
  166. rec->sw_io -= rec->sw_buffer_size;
  167. if (substream->ops->ack)
  168. substream->ops->ack(substream);
  169. return bytes_to_frames(substream->runtime, rec->sw_io);
  170. }
  171. #endif /* __SOUND_PCM_INDIRECT_H */