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