Blame view

include/sound/pcm-indirect.h 5.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
  /*
   * Helper functions for indirect PCM data transfer
   *
   *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
5
   *                   Jaroslav Kysela <perex@perex.cz>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   */
  
  #ifndef __SOUND_PCM_INDIRECT_H
  #define __SOUND_PCM_INDIRECT_H
  
  #include <sound/pcm.h>
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
26
  struct snd_pcm_indirect {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
32
33
34
35
36
  	unsigned int hw_buffer_size;	/* Byte size of hardware buffer */
  	unsigned int hw_queue_size;	/* Max queue size of hw buffer (0 = buffer size) */
  	unsigned int hw_data;	/* Offset to next dst (or src) in hw ring buffer */
  	unsigned int hw_io;	/* Ring buffer hw pointer */
  	int hw_ready;		/* Bytes ready for play (or captured) in hw ring buffer */
  	unsigned int sw_buffer_size;	/* Byte size of software buffer */
  	unsigned int sw_data;	/* Offset to next dst (or src) in sw ring buffer */
  	unsigned int sw_io;	/* Current software pointer in bytes */
  	int sw_ready;		/* Bytes ready to be transferred to/from hw */
  	snd_pcm_uframes_t appl_ptr;	/* Last seen appl_ptr */
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
37
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38

877211f5e   Takashi Iwai   [ALSA] Remove xxx...
39
40
  typedef void (*snd_pcm_indirect_copy_t)(struct snd_pcm_substream *substream,
  					struct snd_pcm_indirect *rec, size_t bytes);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
  
  /*
   * helper function for playback ack callback
   */
962958125   Takashi Iwai   ALSA: pcm: Fix ne...
45
  static inline int
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
46
47
  snd_pcm_indirect_playback_transfer(struct snd_pcm_substream *substream,
  				   struct snd_pcm_indirect *rec,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
  				   snd_pcm_indirect_copy_t copy)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
50
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51
52
53
54
55
56
57
  	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
  	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
  	int qsize;
  
  	if (diff) {
  		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
  			diff += runtime->boundary;
962958125   Takashi Iwai   ALSA: pcm: Fix ne...
58
59
  		if (diff < 0)
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  		rec->sw_ready += (int)frames_to_bytes(runtime, diff);
  		rec->appl_ptr = appl_ptr;
  	}
  	qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
  	while (rec->hw_ready < qsize && rec->sw_ready > 0) {
  		unsigned int hw_to_end = rec->hw_buffer_size - rec->hw_data;
  		unsigned int sw_to_end = rec->sw_buffer_size - rec->sw_data;
  		unsigned int bytes = qsize - rec->hw_ready;
  		if (rec->sw_ready < (int)bytes)
  			bytes = rec->sw_ready;
  		if (hw_to_end < bytes)
  			bytes = hw_to_end;
  		if (sw_to_end < bytes)
  			bytes = sw_to_end;
  		if (! bytes)
  			break;
  		copy(substream, rec, bytes);
  		rec->hw_data += bytes;
  		if (rec->hw_data == rec->hw_buffer_size)
  			rec->hw_data = 0;
  		rec->sw_data += bytes;
  		if (rec->sw_data == rec->sw_buffer_size)
  			rec->sw_data = 0;
  		rec->hw_ready += bytes;
  		rec->sw_ready -= bytes;
  	}
962958125   Takashi Iwai   ALSA: pcm: Fix ne...
86
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
88
89
90
91
92
93
  }
  
  /*
   * helper function for playback pointer callback
   * ptr = current byte pointer
   */
  static inline snd_pcm_uframes_t
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
94
95
  snd_pcm_indirect_playback_pointer(struct snd_pcm_substream *substream,
  				  struct snd_pcm_indirect *rec, unsigned int ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  {
  	int bytes = ptr - rec->hw_io;
  	if (bytes < 0)
  		bytes += rec->hw_buffer_size;
  	rec->hw_io = ptr;
  	rec->hw_ready -= bytes;
  	rec->sw_io += bytes;
  	if (rec->sw_io >= rec->sw_buffer_size)
  		rec->sw_io -= rec->sw_buffer_size;
  	if (substream->ops->ack)
  		substream->ops->ack(substream);
  	return bytes_to_frames(substream->runtime, rec->sw_io);
  }
  
  
  /*
   * helper function for capture ack callback
   */
962958125   Takashi Iwai   ALSA: pcm: Fix ne...
114
  static inline int
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
115
116
  snd_pcm_indirect_capture_transfer(struct snd_pcm_substream *substream,
  				  struct snd_pcm_indirect *rec,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
117
118
  				  snd_pcm_indirect_copy_t copy)
  {
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
119
  	struct snd_pcm_runtime *runtime = substream->runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
120
121
122
123
124
125
  	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
  	snd_pcm_sframes_t diff = appl_ptr - rec->appl_ptr;
  
  	if (diff) {
  		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))
  			diff += runtime->boundary;
962958125   Takashi Iwai   ALSA: pcm: Fix ne...
126
127
  		if (diff < 0)
  			return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
  		rec->sw_ready -= frames_to_bytes(runtime, diff);
  		rec->appl_ptr = appl_ptr;
  	}
  	while (rec->hw_ready > 0 && 
  	       rec->sw_ready < (int)rec->sw_buffer_size) {
  		size_t hw_to_end = rec->hw_buffer_size - rec->hw_data;
  		size_t sw_to_end = rec->sw_buffer_size - rec->sw_data;
  		size_t bytes = rec->sw_buffer_size - rec->sw_ready;
  		if (rec->hw_ready < (int)bytes)
  			bytes = rec->hw_ready;
  		if (hw_to_end < bytes)
  			bytes = hw_to_end;
  		if (sw_to_end < bytes)
  			bytes = sw_to_end;
  		if (! bytes)
  			break;
  		copy(substream, rec, bytes);
  		rec->hw_data += bytes;
  		if ((int)rec->hw_data == rec->hw_buffer_size)
  			rec->hw_data = 0;
  		rec->sw_data += bytes;
  		if (rec->sw_data == rec->sw_buffer_size)
  			rec->sw_data = 0;
  		rec->hw_ready -= bytes;
  		rec->sw_ready += bytes;
  	}
962958125   Takashi Iwai   ALSA: pcm: Fix ne...
154
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
156
157
158
159
160
161
  }
  
  /*
   * helper function for capture pointer callback,
   * ptr = current byte pointer
   */
  static inline snd_pcm_uframes_t
877211f5e   Takashi Iwai   [ALSA] Remove xxx...
162
163
  snd_pcm_indirect_capture_pointer(struct snd_pcm_substream *substream,
  				 struct snd_pcm_indirect *rec, unsigned int ptr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  {
  	int qsize;
  	int bytes = ptr - rec->hw_io;
  	if (bytes < 0)
  		bytes += rec->hw_buffer_size;
  	rec->hw_io = ptr;
  	rec->hw_ready += bytes;
  	qsize = rec->hw_queue_size ? rec->hw_queue_size : rec->hw_buffer_size;
  	if (rec->hw_ready > qsize)
  		return SNDRV_PCM_POS_XRUN;
  	rec->sw_io += bytes;
  	if (rec->sw_io >= rec->sw_buffer_size)
  		rec->sw_io -= rec->sw_buffer_size;
  	if (substream->ops->ack)
  		substream->ops->ack(substream);
  	return bytes_to_frames(substream->runtime, rec->sw_io);
  }
  
  #endif /* __SOUND_PCM_INDIRECT_H */