1 |
/* |
---|
2 |
* ALSA FireWire Plugin |
---|
3 |
* |
---|
4 |
* Copyright (c) 2008 Pieter Palmers <pieter.palmers@ffado.org> |
---|
5 |
* |
---|
6 |
* Based upon the JACK <-> ALSA plugin |
---|
7 |
* Copyright (c) 2003 by Maarten de Boer <mdeboer@iua.upf.es> |
---|
8 |
* 2005 Takashi Iwai <tiwai@suse.de> |
---|
9 |
* |
---|
10 |
* This library is free software; you can redistribute it and/or modify |
---|
11 |
* it under the terms of the GNU Lesser General Public License as |
---|
12 |
* published by the Free Software Foundation; either version 2.1 of |
---|
13 |
* the License, or (at your option) any later version. |
---|
14 |
* |
---|
15 |
* This program is distributed in the hope that it will be useful, |
---|
16 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
17 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
18 |
* GNU Lesser General Public License for more details. |
---|
19 |
* |
---|
20 |
* You should have received a copy of the GNU Lesser General Public |
---|
21 |
* License along with this library; if not, write to the Free Software |
---|
22 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
23 |
* |
---|
24 |
*/ |
---|
25 |
|
---|
26 |
#include <src/debugmodule/debugmodule.h> |
---|
27 |
DECLARE_GLOBAL_DEBUG_MODULE; |
---|
28 |
|
---|
29 |
#include "libutil/IpcRingBuffer.h" |
---|
30 |
#include "libutil/SystemTimeSource.h" |
---|
31 |
using namespace Util; |
---|
32 |
|
---|
33 |
extern "C" { |
---|
34 |
|
---|
35 |
#include "version.h" |
---|
36 |
|
---|
37 |
#include <byteswap.h> |
---|
38 |
#include <sys/shm.h> |
---|
39 |
#include <sys/types.h> |
---|
40 |
#include <sys/socket.h> |
---|
41 |
|
---|
42 |
#include <inttypes.h> |
---|
43 |
|
---|
44 |
#include <pthread.h> |
---|
45 |
|
---|
46 |
#include <alsa/asoundlib.h> |
---|
47 |
#include <alsa/pcm_external.h> |
---|
48 |
|
---|
49 |
#define FFADO_PLUGIN_VERSION "0.0.2" |
---|
50 |
|
---|
51 |
#define PRINT_FUNCTION_ENTRY (printMessage("entering %s\n",__FUNCTION__)) |
---|
52 |
// #define PRINT_FUNCTION_ENTRY |
---|
53 |
|
---|
54 |
typedef struct { |
---|
55 |
snd_pcm_ioplug_t io; |
---|
56 |
|
---|
57 |
int fd; |
---|
58 |
int activated; |
---|
59 |
|
---|
60 |
unsigned int hw_ptr; |
---|
61 |
unsigned int channels; |
---|
62 |
snd_pcm_channel_area_t *areas; |
---|
63 |
|
---|
64 |
snd_pcm_stream_t stream; |
---|
65 |
|
---|
66 |
// thread for polling |
---|
67 |
pthread_t thread; |
---|
68 |
|
---|
69 |
// IPC stuff |
---|
70 |
Util::IpcRingBuffer* buffer; |
---|
71 |
|
---|
72 |
// options |
---|
73 |
long int verbose; |
---|
74 |
snd_pcm_uframes_t period; |
---|
75 |
long int nb_buffers; |
---|
76 |
|
---|
77 |
} snd_pcm_ffado_t; |
---|
78 |
|
---|
79 |
static int snd_pcm_ffado_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) { |
---|
80 |
PRINT_FUNCTION_ENTRY; |
---|
81 |
snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; |
---|
82 |
|
---|
83 |
ffado->channels=0; |
---|
84 |
if (ffado->stream == SND_PCM_STREAM_PLAYBACK) { |
---|
85 |
// HACK |
---|
86 |
ffado->channels=2; |
---|
87 |
} else { |
---|
88 |
// HACK |
---|
89 |
ffado->channels=2; |
---|
90 |
} |
---|
91 |
|
---|
92 |
return 0; |
---|
93 |
} |
---|
94 |
|
---|
95 |
// static snd_pcm_sframes_t snd_pcm_ffado_write(snd_pcm_ioplug_t *io, |
---|
96 |
// const snd_pcm_channel_area_t *areas, |
---|
97 |
// snd_pcm_uframes_t offset, |
---|
98 |
// snd_pcm_uframes_t size) |
---|
99 |
// { |
---|
100 |
// PRINT_FUNCTION_ENTRY; |
---|
101 |
// snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; |
---|
102 |
// IpcRingBuffer::eResult res; |
---|
103 |
// unsigned int i; |
---|
104 |
// |
---|
105 |
// do { |
---|
106 |
// uint32_t *audiobuffers_raw; |
---|
107 |
// res = ffado->buffer->requestBlockForWrite((void**) &audiobuffers_raw); // pointer voodoo |
---|
108 |
// if(res == IpcRingBuffer::eR_OK) { |
---|
109 |
// memset(audiobuffers_raw, 0, ffado->channels * ffado->period * 4); |
---|
110 |
// for (i = 0; i < ffado->channels; i++) { |
---|
111 |
// uint32_t *alsa_data_ptr = (uint32_t *)((char *)areas[i].addr + (areas[i].step * offset / 8)); |
---|
112 |
// uint32_t *ffado_data_ptr = audiobuffers_raw + i*ffado->period; |
---|
113 |
// memcpy(ffado_data_ptr, alsa_data_ptr, ffado->period * 4); |
---|
114 |
// } |
---|
115 |
// // release the block |
---|
116 |
// res = ffado->buffer->releaseBlockForWrite(); |
---|
117 |
// if(res != IpcRingBuffer::eR_OK) { |
---|
118 |
// debugOutput(DEBUG_LEVEL_NORMAL, "PBK: error committing memory block\n"); |
---|
119 |
// break; |
---|
120 |
// } |
---|
121 |
// } else if(res != IpcRingBuffer::eR_Again) { |
---|
122 |
// debugOutput(DEBUG_LEVEL_NORMAL, "PBK: error getting memory block\n"); |
---|
123 |
// } |
---|
124 |
// } while (res == IpcRingBuffer::eR_Again); |
---|
125 |
// |
---|
126 |
// ffado->hw_ptr += ffado->period; |
---|
127 |
// ffado->hw_ptr %= io->buffer_size; |
---|
128 |
// |
---|
129 |
// if(size != ffado->period) { |
---|
130 |
// debugWarning("size %d, period %d, offset %d\n", size, ffado->period, offset); |
---|
131 |
// } else { |
---|
132 |
// debugOutput(DEBUG_LEVEL_NORMAL, "size %d, period %d, offset %d\n", size, ffado->period, offset); |
---|
133 |
// } |
---|
134 |
// |
---|
135 |
// if(res == IpcRingBuffer::eR_OK) { |
---|
136 |
// return ffado->period; |
---|
137 |
// } else { |
---|
138 |
// debugOutput(DEBUG_LEVEL_NORMAL, "error happened\n"); |
---|
139 |
// return -1; |
---|
140 |
// } |
---|
141 |
// } |
---|
142 |
// |
---|
143 |
// static snd_pcm_sframes_t snd_pcm_ffado_read(snd_pcm_ioplug_t *io, |
---|
144 |
// const snd_pcm_channel_area_t *areas, |
---|
145 |
// snd_pcm_uframes_t offset, |
---|
146 |
// snd_pcm_uframes_t size) |
---|
147 |
// { |
---|
148 |
// PRINT_FUNCTION_ENTRY; |
---|
149 |
// snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; |
---|
150 |
// IpcRingBuffer::eResult res; |
---|
151 |
// unsigned int i; |
---|
152 |
// |
---|
153 |
// do { |
---|
154 |
// uint32_t *audiobuffers_raw; |
---|
155 |
// res = ffado->buffer->requestBlockForRead((void**) &audiobuffers_raw); // pointer voodoo |
---|
156 |
// if(res == IpcRingBuffer::eR_OK) { |
---|
157 |
// for (i = 0; i < ffado->channels; i++) { |
---|
158 |
// uint32_t *alsa_data_ptr = (uint32_t *)((char *)areas[i].addr + (areas[i].step * offset / 8)); |
---|
159 |
// uint32_t *ffado_data_ptr = audiobuffers_raw + i*ffado->period; |
---|
160 |
// memcpy(alsa_data_ptr, ffado_data_ptr, ffado->period * 4); |
---|
161 |
// } |
---|
162 |
// // release the block |
---|
163 |
// res = ffado->buffer->releaseBlockForRead(); |
---|
164 |
// if(res != IpcRingBuffer::eR_OK) { |
---|
165 |
// debugOutput(DEBUG_LEVEL_NORMAL, "CAP: error committing memory block\n"); |
---|
166 |
// break; |
---|
167 |
// } |
---|
168 |
// } else if(res != IpcRingBuffer::eR_Again) { |
---|
169 |
// debugOutput(DEBUG_LEVEL_NORMAL, "CAP: error getting memory block\n"); |
---|
170 |
// } |
---|
171 |
// } while (res == IpcRingBuffer::eR_Again); |
---|
172 |
// |
---|
173 |
// ffado->hw_ptr += ffado->period; |
---|
174 |
// ffado->hw_ptr %= io->buffer_size; |
---|
175 |
// |
---|
176 |
// if(res == IpcRingBuffer::eR_OK) { |
---|
177 |
// return ffado->period; |
---|
178 |
// } else { |
---|
179 |
// debugOutput(DEBUG_LEVEL_NORMAL, "error happened\n"); |
---|
180 |
// return -1; |
---|
181 |
// } |
---|
182 |
// } |
---|
183 |
|
---|
184 |
static int |
---|
185 |
snd_pcm_ffado_pollfunction(snd_pcm_ffado_t *ffado) |
---|
186 |
{ |
---|
187 |
|
---|
188 |
// PRINT_FUNCTION_ENTRY; |
---|
189 |
static char buf[1]; |
---|
190 |
snd_pcm_ioplug_t *io = &ffado->io; |
---|
191 |
const snd_pcm_channel_area_t *areas; |
---|
192 |
|
---|
193 |
assert(ffado); |
---|
194 |
assert(ffado->buffer); |
---|
195 |
|
---|
196 |
IpcRingBuffer::eResult res; |
---|
197 |
if (ffado->stream == SND_PCM_STREAM_PLAYBACK) { |
---|
198 |
debugOutput(DEBUG_LEVEL_VERBOSE, "PBK: wait\n"); |
---|
199 |
res = ffado->buffer->waitForWrite(); |
---|
200 |
debugOutput(DEBUG_LEVEL_VERBOSE, "PBK: done, fill: %d\n", ffado->buffer->getBufferFill()); |
---|
201 |
|
---|
202 |
if(res == IpcRingBuffer::eR_OK) { |
---|
203 |
do { |
---|
204 |
uint32_t *audiobuffers_raw; |
---|
205 |
res = ffado->buffer->requestBlockForWrite((void**) &audiobuffers_raw); // pointer voodoo |
---|
206 |
if(res == IpcRingBuffer::eR_OK) { |
---|
207 |
// we have the memory block, do the actual transfer |
---|
208 |
// silence the block |
---|
209 |
memset(audiobuffers_raw, 0, ffado->channels * ffado->period * 4); |
---|
210 |
if(io->state == SND_PCM_STATE_RUNNING) { |
---|
211 |
// get the data address the data comes from |
---|
212 |
areas = snd_pcm_ioplug_mmap_areas(io); |
---|
213 |
|
---|
214 |
// create the list of areas where the data goes to |
---|
215 |
unsigned int channel = 0; |
---|
216 |
for (channel = 0; channel < ffado->channels; channel++) { |
---|
217 |
uint32_t *target = (audiobuffers_raw + channel*ffado->period); |
---|
218 |
ffado->areas[channel].addr = target; |
---|
219 |
ffado->areas[channel].first = 0; |
---|
220 |
ffado->areas[channel].step = 4*8; // FIXME: hardcoded sample size |
---|
221 |
} |
---|
222 |
snd_pcm_uframes_t xfer = 0; |
---|
223 |
while (xfer < ffado->period) { |
---|
224 |
snd_pcm_uframes_t frames = ffado->period - xfer; |
---|
225 |
snd_pcm_uframes_t offset = ffado->hw_ptr; |
---|
226 |
snd_pcm_uframes_t cont = io->buffer_size - offset; |
---|
227 |
|
---|
228 |
if (cont < frames) |
---|
229 |
frames = cont; |
---|
230 |
|
---|
231 |
for (channel = 0; channel < ffado->channels; channel++) { |
---|
232 |
snd_pcm_area_copy(&ffado->areas[channel], xfer, &areas[channel], offset, frames, io->format); |
---|
233 |
} |
---|
234 |
|
---|
235 |
ffado->hw_ptr += frames; |
---|
236 |
ffado->hw_ptr %= io->buffer_size; |
---|
237 |
xfer += frames; |
---|
238 |
} |
---|
239 |
} |
---|
240 |
// release the block |
---|
241 |
res = ffado->buffer->releaseBlockForWrite(); |
---|
242 |
if(res != IpcRingBuffer::eR_OK) { |
---|
243 |
debugOutput(DEBUG_LEVEL_NORMAL, "PBK: error committing memory block\n"); |
---|
244 |
break; |
---|
245 |
} |
---|
246 |
} else if(res != IpcRingBuffer::eR_Again) { |
---|
247 |
debugOutput(DEBUG_LEVEL_NORMAL, "PBK: error getting memory block\n"); |
---|
248 |
} |
---|
249 |
if (res == IpcRingBuffer::eR_Again) { |
---|
250 |
debugWarning("Again\n"); |
---|
251 |
} |
---|
252 |
} while (res == IpcRingBuffer::eR_Again); |
---|
253 |
} else { |
---|
254 |
debugError("Error while waiting\n"); |
---|
255 |
} |
---|
256 |
} else { |
---|
257 |
res = ffado->buffer->waitForRead(); |
---|
258 |
if(res == IpcRingBuffer::eR_OK) { |
---|
259 |
do { |
---|
260 |
uint32_t *audiobuffers_raw; |
---|
261 |
res = ffado->buffer->requestBlockForRead((void**) &audiobuffers_raw); // pointer voodoo |
---|
262 |
if(res == IpcRingBuffer::eR_OK) { |
---|
263 |
// we have the memory block, do the actual transfer |
---|
264 |
if(io->state == SND_PCM_STATE_RUNNING) { |
---|
265 |
// get the data address the data goes to |
---|
266 |
areas = snd_pcm_ioplug_mmap_areas(io); |
---|
267 |
|
---|
268 |
// create the list of areas where the data comes from |
---|
269 |
unsigned int channel = 0; |
---|
270 |
for (channel = 0; channel < io->channels; channel++) { |
---|
271 |
ffado->areas[channel].addr = audiobuffers_raw + channel*ffado->period; |
---|
272 |
ffado->areas[channel].first = 0; |
---|
273 |
ffado->areas[channel].step = 4*8; // FIXME: hardcoded sample size |
---|
274 |
} |
---|
275 |
snd_pcm_uframes_t xfer = 0; |
---|
276 |
while (xfer < ffado->period) { |
---|
277 |
snd_pcm_uframes_t frames = ffado->period - xfer; |
---|
278 |
snd_pcm_uframes_t offset = ffado->hw_ptr; |
---|
279 |
snd_pcm_uframes_t cont = io->buffer_size - offset; |
---|
280 |
|
---|
281 |
if (cont < frames) |
---|
282 |
frames = cont; |
---|
283 |
|
---|
284 |
for (channel = 0; channel < io->channels; channel++) { |
---|
285 |
snd_pcm_area_copy(&areas[channel], offset, &ffado->areas[channel], xfer, frames, io->format); |
---|
286 |
} |
---|
287 |
|
---|
288 |
ffado->hw_ptr += frames; |
---|
289 |
ffado->hw_ptr %= io->buffer_size; |
---|
290 |
xfer += frames; |
---|
291 |
} |
---|
292 |
} |
---|
293 |
// release the block |
---|
294 |
res = ffado->buffer->releaseBlockForRead(); |
---|
295 |
if(res != IpcRingBuffer::eR_OK) { |
---|
296 |
debugOutput(DEBUG_LEVEL_NORMAL, "CAP: error committing memory block\n"); |
---|
297 |
break; |
---|
298 |
} |
---|
299 |
} else if(res != IpcRingBuffer::eR_Again) { |
---|
300 |
debugOutput(DEBUG_LEVEL_NORMAL, "CAP: error getting memory block\n"); |
---|
301 |
} |
---|
302 |
} while (res == IpcRingBuffer::eR_Again); |
---|
303 |
} else { |
---|
304 |
debugError("Error while waiting\n"); |
---|
305 |
} |
---|
306 |
} |
---|
307 |
|
---|
308 |
write(ffado->fd, buf, 1); /* for polling */ |
---|
309 |
|
---|
310 |
if(res == IpcRingBuffer::eR_OK) { |
---|
311 |
return 0; |
---|
312 |
} else { |
---|
313 |
debugOutput(DEBUG_LEVEL_NORMAL, "error happened\n"); |
---|
314 |
return -1; |
---|
315 |
} |
---|
316 |
} |
---|
317 |
|
---|
318 |
// static int |
---|
319 |
// snd_pcm_ffado_pollfunction(snd_pcm_ffado_t *ffado) |
---|
320 |
// { |
---|
321 |
// // wait for a period |
---|
322 |
// IpcRingBuffer::eResult res; |
---|
323 |
// if(ffado->stream == SND_PCM_STREAM_PLAYBACK) { |
---|
324 |
// res = ffado->buffer->waitForWrite(); |
---|
325 |
// } else { |
---|
326 |
// res = ffado->buffer->waitForRead(); |
---|
327 |
// } |
---|
328 |
// |
---|
329 |
// if(res == IpcRingBuffer::eR_OK) { |
---|
330 |
// char buf[1]; |
---|
331 |
// write(ffado->fd, buf, 1); /* for polling */ |
---|
332 |
// return 0; |
---|
333 |
// } else { |
---|
334 |
// return -1; |
---|
335 |
// } |
---|
336 |
// } |
---|
337 |
|
---|
338 |
static void * ffado_workthread(void *arg) |
---|
339 |
{ |
---|
340 |
PRINT_FUNCTION_ENTRY; |
---|
341 |
snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)arg; |
---|
342 |
|
---|
343 |
int oldstate; |
---|
344 |
|
---|
345 |
pthread_setcancelstate (PTHREAD_CANCEL_DEFERRED, &oldstate); |
---|
346 |
|
---|
347 |
while (snd_pcm_ffado_pollfunction(ffado) == 0) { |
---|
348 |
pthread_testcancel(); |
---|
349 |
} |
---|
350 |
return 0; |
---|
351 |
} |
---|
352 |
|
---|
353 |
static int snd_pcm_ffado_poll_revents(snd_pcm_ioplug_t *io, |
---|
354 |
struct pollfd *pfds, unsigned int nfds, |
---|
355 |
unsigned short *revents) |
---|
356 |
{ |
---|
357 |
static char buf[1]; |
---|
358 |
PRINT_FUNCTION_ENTRY; |
---|
359 |
assert(pfds && nfds == 1 && revents); |
---|
360 |
read(pfds[0].fd, buf, 1); |
---|
361 |
*revents = pfds[0].revents; |
---|
362 |
return 0; |
---|
363 |
} |
---|
364 |
|
---|
365 |
static void snd_pcm_ffado_free(snd_pcm_ffado_t *ffado) |
---|
366 |
{ |
---|
367 |
PRINT_FUNCTION_ENTRY; |
---|
368 |
if (ffado) { |
---|
369 |
if (ffado->fd >= 0) close(ffado->fd); |
---|
370 |
if (ffado->io.poll_fd >= 0) close(ffado->io.poll_fd); |
---|
371 |
free(ffado->areas); |
---|
372 |
free(ffado); |
---|
373 |
} |
---|
374 |
} |
---|
375 |
|
---|
376 |
static int snd_pcm_ffado_close(snd_pcm_ioplug_t *io) |
---|
377 |
{ |
---|
378 |
PRINT_FUNCTION_ENTRY; |
---|
379 |
snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; |
---|
380 |
|
---|
381 |
// cleanup the SHM structures here |
---|
382 |
delete ffado->buffer; |
---|
383 |
ffado->buffer = NULL; |
---|
384 |
|
---|
385 |
snd_pcm_ffado_free(ffado); |
---|
386 |
return 0; |
---|
387 |
} |
---|
388 |
|
---|
389 |
static snd_pcm_sframes_t snd_pcm_ffado_pointer(snd_pcm_ioplug_t *io) |
---|
390 |
{ |
---|
391 |
PRINT_FUNCTION_ENTRY; |
---|
392 |
snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; |
---|
393 |
return ffado->hw_ptr; |
---|
394 |
} |
---|
395 |
|
---|
396 |
static int snd_pcm_ffado_start(snd_pcm_ioplug_t *io) |
---|
397 |
{ |
---|
398 |
int result = 0; |
---|
399 |
snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; |
---|
400 |
|
---|
401 |
PRINT_FUNCTION_ENTRY; |
---|
402 |
|
---|
403 |
result = pthread_create (&ffado->thread, 0, ffado_workthread, ffado); |
---|
404 |
if(result) return result; |
---|
405 |
|
---|
406 |
// FIXME: start the SHM stuff |
---|
407 |
return 0; |
---|
408 |
} |
---|
409 |
|
---|
410 |
static int snd_pcm_ffado_stop(snd_pcm_ioplug_t *io) |
---|
411 |
{ |
---|
412 |
snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data; |
---|
413 |
|
---|
414 |
PRINT_FUNCTION_ENTRY; |
---|
415 |
|
---|
416 |
if(pthread_cancel(ffado->thread)) { |
---|
417 |
debugError("could not cancel thread!\n"); |
---|
418 |
} |
---|
419 |
|
---|
420 |
if(pthread_join(ffado->thread,NULL)) { |
---|
421 |
debugError("could not join thread!\n"); |
---|
422 |
} |
---|
423 |
|
---|
424 |
// FIXME: stop the SHM client |
---|
425 |
return 0; |
---|
426 |
} |
---|
427 |
|
---|
428 |
static int snd_pcm_ffado_prepare(snd_pcm_ioplug_t *io) |
---|
429 |
{ |
---|
430 |
PRINT_FUNCTION_ENTRY; |
---|
431 |
return 0; |
---|
432 |
} |
---|
433 |
|
---|
434 |
static snd_pcm_ioplug_callback_t ffado_pcm_callback; |
---|
435 |
|
---|
436 |
#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0])) |
---|
437 |
|
---|
438 |
static int ffado_set_hw_constraint(snd_pcm_ffado_t *ffado) |
---|
439 |
{ |
---|
440 |
PRINT_FUNCTION_ENTRY; |
---|
441 |
unsigned int access_list[] = { |
---|
442 |
SND_PCM_ACCESS_MMAP_NONINTERLEAVED, |
---|
443 |
// SND_PCM_ACCESS_RW_NONINTERLEAVED, |
---|
444 |
}; |
---|
445 |
|
---|
446 |
unsigned int rate_list[1]; |
---|
447 |
|
---|
448 |
unsigned int format = SND_PCM_FORMAT_S24; |
---|
449 |
int err; |
---|
450 |
|
---|
451 |
// FIXME: make all of the parameters dynamic instead of static |
---|
452 |
rate_list[0] = 48000; |
---|
453 |
|
---|
454 |
// setup the plugin capabilities |
---|
455 |
if ((err = snd_pcm_ioplug_set_param_list(&ffado->io, SND_PCM_IOPLUG_HW_ACCESS, |
---|
456 |
ARRAY_SIZE(access_list), access_list)) < 0 || |
---|
457 |
(err = snd_pcm_ioplug_set_param_list(&ffado->io, SND_PCM_IOPLUG_HW_RATE, |
---|
458 |
ARRAY_SIZE(rate_list), rate_list)) < 0 || |
---|
459 |
(err = snd_pcm_ioplug_set_param_list(&ffado->io, SND_PCM_IOPLUG_HW_FORMAT, |
---|
460 |
1, &format)) < 0 || |
---|
461 |
(err = snd_pcm_ioplug_set_param_minmax(&ffado->io, SND_PCM_IOPLUG_HW_CHANNELS, |
---|
462 |
ffado->channels, ffado->channels)) < 0 || |
---|
463 |
(err = snd_pcm_ioplug_set_param_minmax(&ffado->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, |
---|
464 |
ffado->period, ffado->period)) < 0 || |
---|
465 |
(err = snd_pcm_ioplug_set_param_minmax(&ffado->io, SND_PCM_IOPLUG_HW_PERIODS, |
---|
466 |
ffado->nb_buffers, ffado->nb_buffers)) < 0) |
---|
467 |
return err; |
---|
468 |
|
---|
469 |
return 0; |
---|
470 |
} |
---|
471 |
|
---|
472 |
static int snd_pcm_ffado_open(snd_pcm_t **pcmp, const char *name, |
---|
473 |
snd_pcm_stream_t stream, int mode) |
---|
474 |
{ |
---|
475 |
|
---|
476 |
PRINT_FUNCTION_ENTRY; |
---|
477 |
snd_pcm_ffado_t *ffado; |
---|
478 |
int err; |
---|
479 |
int fd[2]; |
---|
480 |
|
---|
481 |
assert(pcmp); |
---|
482 |
|
---|
483 |
ffado = (snd_pcm_ffado_t *)calloc(1, sizeof(*ffado)); |
---|
484 |
if (!ffado) |
---|
485 |
return -ENOMEM; |
---|
486 |
|
---|
487 |
ffado->stream=stream; |
---|
488 |
|
---|
489 |
// discover the devices to discover the capabilities |
---|
490 |
// get the SHM structure |
---|
491 |
|
---|
492 |
socketpair(AF_LOCAL, SOCK_STREAM, 0, fd); |
---|
493 |
|
---|
494 |
ffado->fd = fd[0]; |
---|
495 |
|
---|
496 |
// initialize callback struct |
---|
497 |
ffado_pcm_callback.close = snd_pcm_ffado_close; |
---|
498 |
ffado_pcm_callback.start = snd_pcm_ffado_start; |
---|
499 |
ffado_pcm_callback.stop = snd_pcm_ffado_stop; |
---|
500 |
ffado_pcm_callback.pointer = snd_pcm_ffado_pointer; |
---|
501 |
ffado_pcm_callback.hw_params = snd_pcm_ffado_hw_params; |
---|
502 |
ffado_pcm_callback.prepare = snd_pcm_ffado_prepare; |
---|
503 |
ffado_pcm_callback.poll_revents = snd_pcm_ffado_poll_revents; |
---|
504 |
// if (stream == SND_PCM_STREAM_PLAYBACK) { |
---|
505 |
// ffado_pcm_callback.transfer = snd_pcm_ffado_write; |
---|
506 |
// } else { |
---|
507 |
// ffado_pcm_callback.transfer = snd_pcm_ffado_read; |
---|
508 |
// } |
---|
509 |
|
---|
510 |
// prepare io struct |
---|
511 |
ffado->io.version = SND_PCM_IOPLUG_VERSION; |
---|
512 |
ffado->io.name = "FFADO PCM Plugin"; |
---|
513 |
ffado->io.callback = &ffado_pcm_callback; |
---|
514 |
ffado->io.private_data = ffado; |
---|
515 |
// ffado->io.mmap_rw = 0; |
---|
516 |
ffado->io.mmap_rw = 1; |
---|
517 |
ffado->io.poll_fd = fd[1]; |
---|
518 |
ffado->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN; |
---|
519 |
|
---|
520 |
err = snd_pcm_ioplug_create(&ffado->io, name, stream, mode); |
---|
521 |
if (err < 0) { |
---|
522 |
snd_pcm_ffado_free(ffado); |
---|
523 |
return err; |
---|
524 |
} |
---|
525 |
|
---|
526 |
err = ffado_set_hw_constraint(ffado); |
---|
527 |
if (err < 0) { |
---|
528 |
snd_pcm_ioplug_delete(&ffado->io); |
---|
529 |
return err; |
---|
530 |
} |
---|
531 |
|
---|
532 |
*pcmp = ffado->io.pcm; |
---|
533 |
|
---|
534 |
// these are the params |
---|
535 |
ffado->channels = 2; |
---|
536 |
ffado->period = 1024; |
---|
537 |
ffado->verbose = 6; |
---|
538 |
ffado->nb_buffers = 5; |
---|
539 |
|
---|
540 |
setDebugLevel(ffado->verbose); |
---|
541 |
|
---|
542 |
ffado->areas = (snd_pcm_channel_area_t *)calloc(ffado->channels, sizeof(snd_pcm_channel_area_t)); |
---|
543 |
if (!ffado->areas) { |
---|
544 |
snd_pcm_ffado_free(ffado); |
---|
545 |
return -ENOMEM; |
---|
546 |
} |
---|
547 |
|
---|
548 |
// prepare the IPC buffer |
---|
549 |
unsigned int buffsize = ffado->channels * ffado->period * 4; |
---|
550 |
if(stream == SND_PCM_STREAM_PLAYBACK) { |
---|
551 |
ffado->buffer = new IpcRingBuffer("playbackbuffer", |
---|
552 |
IpcRingBuffer::eBT_Slave, |
---|
553 |
IpcRingBuffer::eD_Outward, |
---|
554 |
IpcRingBuffer::eB_Blocking, |
---|
555 |
ffado->nb_buffers, buffsize); |
---|
556 |
if(ffado->buffer == NULL) { |
---|
557 |
debugError("Could not create playbackbuffer\n"); |
---|
558 |
return -1; |
---|
559 |
} |
---|
560 |
if(!ffado->buffer->init()) { |
---|
561 |
debugError("Could not init playbackbuffer\n"); |
---|
562 |
delete ffado->buffer; |
---|
563 |
ffado->buffer = NULL; |
---|
564 |
return -1; |
---|
565 |
} |
---|
566 |
ffado->buffer->setVerboseLevel(ffado->verbose); |
---|
567 |
} else { |
---|
568 |
ffado->buffer = new IpcRingBuffer("capturebuffer", |
---|
569 |
IpcRingBuffer::eBT_Slave, |
---|
570 |
IpcRingBuffer::eD_Inward, |
---|
571 |
IpcRingBuffer::eB_Blocking, |
---|
572 |
ffado->nb_buffers, buffsize); |
---|
573 |
if(ffado->buffer == NULL) { |
---|
574 |
debugError("Could not create capturebuffer\n"); |
---|
575 |
return -1; |
---|
576 |
} |
---|
577 |
if(!ffado->buffer->init()) { |
---|
578 |
debugError("Could not init capturebuffer\n"); |
---|
579 |
delete ffado->buffer; |
---|
580 |
ffado->buffer = NULL; |
---|
581 |
return -1; |
---|
582 |
} |
---|
583 |
ffado->buffer->setVerboseLevel(ffado->verbose); |
---|
584 |
} |
---|
585 |
|
---|
586 |
return 0; |
---|
587 |
} |
---|
588 |
|
---|
589 |
SND_PCM_PLUGIN_DEFINE_FUNC(ffado) |
---|
590 |
{ |
---|
591 |
printMessage("FireWire plugin for ALSA\n version %s compiled %s %s\n using %s\n", |
---|
592 |
FFADO_PLUGIN_VERSION, __DATE__, __TIME__, PACKAGE_STRING); |
---|
593 |
|
---|
594 |
snd_config_iterator_t i, next; |
---|
595 |
int err; |
---|
596 |
|
---|
597 |
snd_config_for_each(i, next, conf) { |
---|
598 |
snd_config_t *n = snd_config_iterator_entry(i); |
---|
599 |
const char *id; |
---|
600 |
if (snd_config_get_id(n, &id) < 0) |
---|
601 |
continue; |
---|
602 |
if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) |
---|
603 |
continue; |
---|
604 |
|
---|
605 |
SNDERR("Unknown field %s", id); |
---|
606 |
return -EINVAL; |
---|
607 |
} |
---|
608 |
|
---|
609 |
err = snd_pcm_ffado_open(pcmp, name, stream, mode); |
---|
610 |
|
---|
611 |
return err; |
---|
612 |
|
---|
613 |
} |
---|
614 |
|
---|
615 |
SND_PCM_PLUGIN_SYMBOL(ffado); |
---|
616 |
|
---|
617 |
} // extern "C" |
---|