37 do { fprintf(stderr, "virtio-audio: error: " fmt , ##args);} while (0) |
37 do { fprintf(stderr, "virtio-audio: error: " fmt , ##args);} while (0) |
38 #endif |
38 #endif |
39 |
39 |
40 #define NUM_STREAMS 2 |
40 #define NUM_STREAMS 2 |
41 |
41 |
|
42 #define VIRT_CONTROL_QUEUE_SIZE 0x40 |
|
43 #define VIRT_DATA_QUEUE_SIZE 0x80 |
|
44 |
42 typedef struct { |
45 typedef struct { |
43 struct VirtIOAudio *dev; |
46 struct VirtIOAudio *dev; |
44 VirtQueue *data_vq; |
47 VirtQueue *data_vq; |
45 struct audsettings fmt; |
48 struct audsettings fmt; |
46 SWVoiceOut *out_voice; |
49 SWVoiceOut *out_voice; |
95 int iov_len; |
98 int iov_len; |
96 |
99 |
97 if (stream->in_voice) { |
100 if (stream->in_voice) { |
98 iov_len = stream->elem.in_num; |
101 iov_len = stream->elem.in_num; |
99 iov = stream->elem.in_sg; |
102 iov = stream->elem.in_sg; |
100 } else { |
103 } else if (stream->out_voice) { |
101 iov_len = stream->elem.out_num; |
104 iov_len = stream->elem.out_num; |
102 iov = stream->elem.out_sg; |
105 iov = stream->elem.out_sg; |
|
106 } else |
|
107 { |
|
108 DPRINTF("No voice selected skipping block\n"); |
|
109 return 0; |
103 } |
110 } |
104 written = 0; |
111 written = 0; |
105 for (n = 0; total_len > 0 && n < iov_len; n++) { |
112 for (n = 0; total_len > 0 && n < iov_len; n++) { |
106 p = iov[n].iov_base; |
113 p = iov[n].iov_base; |
107 to_write = iov[n].iov_len; |
114 to_write = iov[n].iov_len; |
117 if (to_write > total_len) |
124 if (to_write > total_len) |
118 to_write = total_len; |
125 to_write = total_len; |
119 while (to_write) { |
126 while (to_write) { |
120 if (stream->in_voice) { |
127 if (stream->in_voice) { |
121 size = AUD_read(stream->in_voice, p, to_write); |
128 size = AUD_read(stream->in_voice, p, to_write); |
|
129 } else if (stream->out_voice) { |
|
130 size = AUD_write(stream->out_voice, p, to_write); |
122 } else { |
131 } else { |
123 size = AUD_write(stream->out_voice, p, to_write); |
132 size = 0; |
124 } |
133 } |
125 DPRINTF("Copied %d/%d\n", size, to_write); |
134 DPRINTF("Copied %d/%d\n", size, to_write); |
126 if (size == 0) { |
135 if (size == 0) { |
127 total_len = 0; |
136 total_len = 0; |
128 break; |
137 break; |
139 { |
148 { |
140 VirtIOAudioStream *stream = opaque; |
149 VirtIOAudioStream *stream = opaque; |
141 int n; |
150 int n; |
142 |
151 |
143 DPRINTF("Callback (%d)\n", avail); |
152 DPRINTF("Callback (%d)\n", avail); |
|
153 if ((!stream->in_voice)&&(!stream->out_voice)) |
|
154 { |
|
155 DPRINTF("Skipping callback as no voice is selected!\n"); |
|
156 } |
144 while (avail) { |
157 while (avail) { |
145 while (stream->data_left == 0) { |
158 while (stream->data_left == 0) { |
146 if (stream->has_buffer) { |
159 if (stream->has_buffer) { |
147 virtqueue_push(stream->data_vq, &stream->elem, 0); |
160 virtqueue_push(stream->data_vq, &stream->elem, stream->data_offset); |
148 virtio_notify(&stream->dev->vdev, stream->data_vq); |
161 virtio_notify(&stream->dev->vdev, stream->data_vq); |
149 stream->has_buffer = 0; |
162 stream->has_buffer = 0; |
150 } |
163 } |
151 if (!virtqueue_pop(stream->data_vq, &stream->elem)) { |
164 if (!virtqueue_pop(stream->data_vq, &stream->elem)) { |
152 /* Buffer underrun. */ |
165 /* Buffer underrun. */ |
158 stream->data_left = 0; |
171 stream->data_left = 0; |
159 stream->has_buffer = 1; |
172 stream->has_buffer = 1; |
160 if (stream->in_voice) { |
173 if (stream->in_voice) { |
161 for (n = 0; n < stream->elem.in_num; n++) |
174 for (n = 0; n < stream->elem.in_num; n++) |
162 stream->data_left += stream->elem.in_sg[n].iov_len; |
175 stream->data_left += stream->elem.in_sg[n].iov_len; |
163 } else { |
176 } else if (stream->out_voice) { |
164 for (n = 0; n < stream->elem.out_num; n++) |
177 for (n = 0; n < stream->elem.out_num; n++) |
165 stream->data_left += stream->elem.out_sg[n].iov_len; |
178 stream->data_left += stream->elem.out_sg[n].iov_len; |
166 } |
179 } |
167 } |
180 } |
168 if (stream->data_left == 0) |
181 if (stream->data_left == 0) |
173 avail -= n; |
186 avail -= n; |
174 if (!n) |
187 if (!n) |
175 break; |
188 break; |
176 } |
189 } |
177 if (stream->data_left == 0 && stream->has_buffer) { |
190 if (stream->data_left == 0 && stream->has_buffer) { |
178 virtqueue_push(stream->data_vq, &stream->elem, 0); |
191 virtqueue_push(stream->data_vq, &stream->elem, stream->data_offset); |
179 virtio_notify(&stream->dev->vdev, stream->data_vq); |
192 virtio_notify(&stream->dev->vdev, stream->data_vq); |
180 stream->has_buffer = 0; |
193 stream->has_buffer = 0; |
181 } |
194 } |
182 } |
195 } |
183 |
196 |
218 int len; |
231 int len; |
219 size_t out_bytes; |
232 size_t out_bytes; |
220 uint32_t value; |
233 uint32_t value; |
221 |
234 |
222 while (virtqueue_pop(s->cmd_vq, &elem)) { |
235 while (virtqueue_pop(s->cmd_vq, &elem)) { |
|
236 size_t bytes_transferred = 0; |
223 for (out_n = 0; out_n < elem.out_num; out_n++) { |
237 for (out_n = 0; out_n < elem.out_num; out_n++) { |
224 p = (uint32_t *)elem.out_sg[out_n].iov_base; |
238 p = (uint32_t *)elem.out_sg[out_n].iov_base; |
225 len = elem.out_sg[out_n].iov_len; |
239 len = elem.out_sg[out_n].iov_len; |
226 while (len > 0) { |
240 while (len > 0) { |
227 if (len < 12) { |
241 if (len < 12) { |
247 break; |
261 break; |
248 case VIRTIO_AUDIO_CMD_SET_FREQ: |
262 case VIRTIO_AUDIO_CMD_SET_FREQ: |
249 stream->fmt.freq = value; |
263 stream->fmt.freq = value; |
250 break; |
264 break; |
251 case VIRTIO_AUDIO_CMD_INIT: |
265 case VIRTIO_AUDIO_CMD_INIT: |
252 if (value & 1) { |
266 out_bytes = 0; |
|
267 if (value == 1) { |
253 if (stream->out_voice) { |
268 if (stream->out_voice) { |
254 AUD_close_out(&s->card, stream->out_voice); |
269 AUD_close_out(&s->card, stream->out_voice); |
255 stream->out_voice = NULL; |
270 stream->out_voice = NULL; |
256 } |
271 } |
257 stream->in_voice = |
272 stream->in_voice = |
259 "virtio-audio.in", |
274 "virtio-audio.in", |
260 stream, |
275 stream, |
261 virtio_audio_callback, |
276 virtio_audio_callback, |
262 &stream->fmt); |
277 &stream->fmt); |
263 virtio_audio_cmd_result(0, &elem, &out_bytes); |
278 virtio_audio_cmd_result(0, &elem, &out_bytes); |
264 } else { |
279 } else if (value == 0) { |
265 if (stream->out_voice) { |
280 if (stream->in_voice) { |
266 AUD_close_in(&s->card, stream->in_voice); |
281 AUD_close_in(&s->card, stream->in_voice); |
267 stream->in_voice = NULL; |
282 stream->in_voice = NULL; |
268 } |
283 } |
269 stream->out_voice = |
284 stream->out_voice = |
270 AUD_open_out(&s->card, stream->out_voice, |
285 AUD_open_out(&s->card, stream->out_voice, |
272 stream, |
287 stream, |
273 virtio_audio_callback, |
288 virtio_audio_callback, |
274 &stream->fmt); |
289 &stream->fmt); |
275 value = AUD_get_buffer_size_out(stream->out_voice); |
290 value = AUD_get_buffer_size_out(stream->out_voice); |
276 virtio_audio_cmd_result(value, &elem, &out_bytes); |
291 virtio_audio_cmd_result(value, &elem, &out_bytes); |
|
292 } else { // let us close all down |
|
293 if (stream->out_voice) { |
|
294 AUD_close_out(&s->card, stream->out_voice); |
|
295 stream->out_voice = NULL; |
|
296 } |
|
297 if (stream->in_voice) { |
|
298 AUD_close_in(&s->card, stream->in_voice); |
|
299 stream->in_voice = NULL; |
|
300 } |
277 } |
301 } |
|
302 bytes_transferred += out_bytes; |
278 break; |
303 break; |
279 case VIRTIO_AUDIO_CMD_RUN: |
304 case VIRTIO_AUDIO_CMD_RUN: |
280 if (stream->in_voice) { |
305 if (stream->in_voice) { |
281 AUD_set_active_in(stream->in_voice, value); |
306 AUD_set_active_in(stream->in_voice, value); |
282 } else { |
307 } else if (stream->out_voice) { |
283 AUD_set_active_out(stream->out_voice, value); |
308 AUD_set_active_out(stream->out_voice, value); |
|
309 } else |
|
310 { |
|
311 DPRINTF("Cannot execute CMD_RUN as no voice is active\n"); |
284 } |
312 } |
285 break; |
313 break; |
286 } |
314 } |
287 p += 3; |
315 p += 3; |
288 len -= 12; |
316 len -= 12; |
289 } |
317 bytes_transferred += 12; |
290 } |
318 } |
291 virtqueue_push(s->cmd_vq, &elem, out_bytes); |
319 } |
|
320 virtqueue_push(s->cmd_vq, &elem, bytes_transferred); |
|
321 virtio_notify(vdev, s->cmd_vq); |
292 } |
322 } |
293 } |
323 } |
294 |
324 |
295 static void virtio_audio_handle_data(VirtIODevice *vdev, VirtQueue *vq) |
325 static void virtio_audio_handle_data(VirtIODevice *vdev, VirtQueue *vq) |
296 { |
326 { |
392 return; |
422 return; |
393 |
423 |
394 s->vdev.get_config = virtio_audio_get_config; |
424 s->vdev.get_config = virtio_audio_get_config; |
395 s->vdev.get_features = virtio_audio_get_features; |
425 s->vdev.get_features = virtio_audio_get_features; |
396 s->vdev.set_features = virtio_audio_set_features; |
426 s->vdev.set_features = virtio_audio_set_features; |
397 s->cmd_vq = virtio_add_queue(&s->vdev, 64, virtio_audio_handle_cmd); |
427 s->cmd_vq = virtio_add_queue(&s->vdev, VIRT_CONTROL_QUEUE_SIZE, virtio_audio_handle_cmd); |
398 for (i = 0; i < NUM_STREAMS; i++) { |
428 for (i = 0; i < NUM_STREAMS; i++) { |
399 s->stream[i].data_vq = virtio_add_queue(&s->vdev, 128, |
429 s->stream[i].data_vq = virtio_add_queue(&s->vdev, VIRT_DATA_QUEUE_SIZE, |
400 virtio_audio_handle_data); |
430 virtio_audio_handle_data); |
401 s->stream[i].dev = s; |
431 s->stream[i].dev = s; |
402 } |
432 } |
403 |
433 |
404 AUD_register_card(audio, "virtio-audio", &s->card); |
434 AUD_register_card(audio, "virtio-audio", &s->card); |