| Joachim Foerster | a9f00d8 | 2007-11-05 16:06:01 +0100 | [diff] [blame] | 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 |  | 
| Joachim Foerster | a9f00d8 | 2007-11-05 16:06:01 +0100 | [diff] [blame] | 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 |  | 
 | 40 | void 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 |  */ | 
 | 135 | static void | 
 | 136 | snd_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 |  */ | 
 | 231 | snd_pcm_uframes_t | 
 | 232 | snd_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 |  */ | 
 | 244 | static void | 
 | 245 | snd_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 transfered 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 transfered 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 |  */ | 
 | 373 | void | 
 | 374 | snd_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 | 
| Joachim Foerster | dddefd0 | 2007-11-05 15:48:36 +0100 | [diff] [blame] | 404 | 		rec->min_periods = (rec->min_periods % rec->min_multiple); | 
| Joachim Foerster | a9f00d8 | 2007-11-05 16:06:01 +0100 | [diff] [blame] | 405 | 		snd_pcm_period_elapsed(substream); | 
 | 406 | 	} | 
 | 407 | } | 
 | 408 |  | 
 | 409 | /* | 
 | 410 |  * _internal_ helper function for capture interrupt callback | 
 | 411 |  */ | 
 | 412 | static void | 
 | 413 | snd_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 |  */ | 
 | 536 | void | 
 | 537 | snd_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++; | 
| Joachim Foerster | a9f00d8 | 2007-11-05 16:06:01 +0100 | [diff] [blame] | 569 | #endif | 
| Joachim Foerster | dddefd0 | 2007-11-05 15:48:36 +0100 | [diff] [blame] | 570 | 		rec->min_periods = (rec->min_periods % rec->min_multiple); | 
| Joachim Foerster | a9f00d8 | 2007-11-05 16:06:01 +0100 | [diff] [blame] | 571 | 		snd_pcm_period_elapsed(substream); | 
 | 572 | 	} | 
 | 573 | } |