1 /* 2 * Helper functions for indirect PCM data transfer 3 * 4 * Copyright (c) by Takashi Iwai <tiwai (at) suse.de> 5 * Jaroslav Kysela <perex (at) 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 22 #ifndef __SOUND_PCM_INDIRECT_H 23 #define __SOUND_PCM_INDIRECT_H 24 25 #include <sound/pcm.h> 26 27 struct snd_pcm_indirect { 28 unsigned int hw_buffer_size; /* Byte size of hardware buffer */ 29 unsigned int hw_queue_size; /* Max queue size of hw buffer (0 = buffer size) */ 30 unsigned int hw_data; /* Offset to next dst (or src) in hw ring buffer */ 31 unsigned int hw_io; /* Ring buffer hw pointer */ 32 int hw_ready; /* Bytes ready for play (or captured) in hw ring buffer */ 33 unsigned int sw_buffer_size; /* Byte size of software buffer */ 34 unsigned int sw_data; /* Offset to next dst (or src) in sw ring buffer */ 35 unsigned int sw_io; /* Current software pointer in bytes */ 36 int sw_ready; /* Bytes ready to be transferred to/from hw */ 37 snd_pcm_uframes_t appl_ptr; /* Last seen appl_ptr */ 38 }; 39 40 typedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream, 41 struct snd_pcm_indirect *rec, size_t bytes); 42 43 /* 44 * helper function for playback ack callback 45 */ 46 static inline void 47 snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream, 48 struct snd_pcm_indirect *rec, 49 snd_pcm_indirect_copy_t copy) 50 { 51 struct snd_pcm_runtime *runtime = substream->runtime; 52 snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; 53 snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; 54 int qsize; 55 56 if (diff) { 57 if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) 58 diff += runtime->boundary; 59 rec->sw_ready += (int)frames_to_bytes(runtime, diff); 60 rec->appl_ptr = appl_ptr; 61 } 62 qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size; 63 while (rec->hw_ready < qsize && rec->sw_ready > 0) { 64 unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data; 65 unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data; 66 unsigned int bytes = qsize - rec->hw_ready; 67 if (rec->sw_ready < (int)bytes) 68 bytes = rec->sw_ready; 69 if (hw_to_end < bytes) 70 bytes = hw_to_end; 71 if (sw_to_end < bytes) 72 bytes = sw_to_end; 73 if (! bytes) 74 break; 75 copy(substream, rec, bytes); 76 rec->hw_data += bytes; 77 if (rec->hw_data == rec->hw_buffer_size) 78 rec->hw_data = 0; 79 rec->sw_data += bytes; 80 if (rec->sw_data == rec->sw_buffer_size) 81 rec->sw_data = 0; 82 rec->hw_ready += bytes; 83 rec->sw_ready -= bytes; 84 } 85 } 86 87 /* 88 * helper function for playback pointer callback 89 * ptr = current byte pointer 90 */ 91 static inline snd_pcm_uframes_t 92 snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream, 93 struct snd_pcm_indirect *rec, unsigned int ptr) 94 { 95 int bytes = ptr - rec->hw_io; 96 if (bytes < 0) 97 bytes += rec->hw_buffer_size; 98 rec->hw_io = ptr; 99 rec->hw_ready -= bytes; 100 rec->sw_io += bytes; 101 if (rec->sw_io >= rec->sw_buffer_size) 102 rec->sw_io -= rec->sw_buffer_size; 103 if (substream->ops->ack) 104 substream->ops->ack(substream); 105 return bytes_to_frames(substream->runtime, rec->sw_io); 106 } 107 108 109 /* 110 * helper function for capture ack callback 111 */ 112 static inline void 113 snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream, 114 struct snd_pcm_indirect *rec, 115 snd_pcm_indirect_copy_t copy) 116 { 117 struct snd_pcm_runtime *runtime = substream->runtime; 118 snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; 119 snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr; 120 121 if (diff) { 122 if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) 123 diff += runtime->boundary; 124 rec->sw_ready -= frames_to_bytes(runtime, diff); 125 rec->appl_ptr = appl_ptr; 126 } 127 while (rec->hw_ready > 0 && 128 rec->sw_ready < (int)rec->sw_buffer_size) { 129 size_t hw_to_end = rec->hw_buffer_size - rec->hw_data; 130 size_t sw_to_end = rec->sw_buffer_size - rec->sw_data; 131 size_t bytes = rec->sw_buffer_size - rec->sw_ready; 132 if (rec->hw_ready < (int)bytes) 133 bytes = rec->hw_ready; 134 if (hw_to_end < bytes) 135 bytes = hw_to_end; 136 if (sw_to_end < bytes) 137 bytes = sw_to_end; 138 if (! bytes) 139 break; 140 copy(substream, rec, bytes); 141 rec->hw_data += bytes; 142 if ((int)rec->hw_data == rec->hw_buffer_size) 143 rec->hw_data = 0; 144 rec->sw_data += bytes; 145 if (rec->sw_data == rec->sw_buffer_size) 146 rec->sw_data = 0; 147 rec->hw_ready -= bytes; 148 rec->sw_ready += bytes; 149 } 150 } 151 152 /* 153 * helper function for capture pointer callback, 154 * ptr = current byte pointer 155 */ 156 static inline snd_pcm_uframes_t 157 snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream, 158 struct snd_pcm_indirect *rec, unsigned int ptr) 159 { 160 int qsize; 161 int bytes = ptr - rec->hw_io; 162 if (bytes < 0) 163 bytes += rec->hw_buffer_size; 164 rec->hw_io = ptr; 165 rec->hw_ready += bytes; 166 qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size; 167 if (rec->hw_ready > qsize) 168 return SNDRV_PCM_POS_XRUN; 169 rec->sw_io += bytes; 170 if (rec->sw_io >= rec->sw_buffer_size) 171 rec->sw_io -= rec->sw_buffer_size; 172 if (substream->ops->ack) 173 substream->ops->ack(substream); 174 return bytes_to_frames(substream->runtime, rec->sw_io); 175 } 176 177 #endif /* __SOUND_PCM_INDIRECT_H */ 178