root/trunk/libffado/support/alsa/alsa_plugin.cpp

Revision 1240, 12.7 kB (checked in by ppalmers, 13 years ago)

fix bugs in IPC comms. Add preliminary FFADO-IPC ALSA plugin

Line 
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 "config.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.1"
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 period_size;
62     unsigned int channels;
63     snd_pcm_channel_area_t *areas;
64
65     snd_pcm_stream_t stream;
66
67     // thread for polling
68     pthread_t thread;
69
70     // IPC stuff
71     Util::IpcRingBuffer* buffer;
72
73     // options
74     long int verbose;
75     long int period;
76     long int nb_buffers;
77
78 } snd_pcm_ffado_t;
79
80 static int snd_pcm_ffado_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) {
81     PRINT_FUNCTION_ENTRY;
82     snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data;
83
84     ffado->channels=0;
85     if (ffado->stream == SND_PCM_STREAM_PLAYBACK) {
86         // HACK
87         ffado->channels=2;
88     } else {
89         // HACK
90         ffado->channels=2;
91     }
92
93     return 0;
94 }
95
96 static int
97 snd_pcm_ffado_pollfunction(snd_pcm_ffado_t *ffado)
98 {
99
100 //      PRINT_FUNCTION_ENTRY;
101     static char buf[1];
102     snd_pcm_ioplug_t *io = &ffado->io;
103     const snd_pcm_channel_area_t *areas;
104     unsigned int i;
105
106     assert(ffado);
107     assert(ffado->buffer);
108
109     // wait for data to become available
110     // FIXME
111
112     // get the data address everything should go to/come from
113     areas = snd_pcm_ioplug_mmap_areas(io);
114
115     // get the current offset
116     snd_pcm_uframes_t offset = ffado->hw_ptr;
117
118     IpcRingBuffer::eResult res;
119     if (ffado->stream == SND_PCM_STREAM_PLAYBACK) {
120         do {
121             uint32_t *audiobuffers_raw;
122             res = ffado->buffer->requestBlockForWrite((void**) &audiobuffers_raw); // pointer voodoo
123             if(res == IpcRingBuffer::eR_OK) {
124                 memset(audiobuffers_raw, 0, ffado->channels * ffado->period * 4);
125                 for (i = 0; i < ffado->channels; i++) {
126                     uint32_t *alsa_data_ptr = (uint32_t *)((char *)areas[i].addr + (areas[i].step * offset / 8));
127                     uint32_t *ffado_data_ptr = audiobuffers_raw + i*ffado->period;
128                     memcpy(ffado_data_ptr, alsa_data_ptr, ffado->period * 4);
129                 }
130                 // release the block
131                 res = ffado->buffer->releaseBlockForWrite();
132                 if(res != IpcRingBuffer::eR_OK) {
133                     debugOutput(DEBUG_LEVEL_NORMAL, "PBK: error committing memory block\n");
134                     break;
135                 }
136             } else if(res != IpcRingBuffer::eR_Again) {
137                 debugOutput(DEBUG_LEVEL_NORMAL, "PBK: error getting memory block\n");
138             }
139         } while (res == IpcRingBuffer::eR_Again);
140     } else {
141         do {
142             uint32_t *audiobuffers_raw;
143             res = ffado->buffer->requestBlockForRead((void**) &audiobuffers_raw); // pointer voodoo
144             if(res == IpcRingBuffer::eR_OK) {
145                 for (i = 0; i < ffado->channels; i++) {
146                     uint32_t *alsa_data_ptr = (uint32_t *)((char *)areas[i].addr + (areas[i].step * offset / 8));
147                     uint32_t *ffado_data_ptr = audiobuffers_raw + i*ffado->period;
148                     memcpy(alsa_data_ptr, ffado_data_ptr, ffado->period * 4);
149                 }
150                 // release the block
151                 res = ffado->buffer->releaseBlockForRead();
152                 if(res != IpcRingBuffer::eR_OK) {
153                     debugOutput(DEBUG_LEVEL_NORMAL, "CAP: error committing memory block\n");
154                     break;
155                 }
156             } else if(res != IpcRingBuffer::eR_Again) {
157                 debugOutput(DEBUG_LEVEL_NORMAL, "CAP: error getting memory block\n");
158             }
159         } while (res == IpcRingBuffer::eR_Again);
160     }
161
162     ffado->hw_ptr += ffado->period;
163     ffado->hw_ptr %= io->buffer_size;
164
165     write(ffado->fd, buf, 1); /* for polling */
166
167     if(res == IpcRingBuffer::eR_OK) {
168         return 0;
169     } else {
170         debugOutput(DEBUG_LEVEL_NORMAL, "error happened\n");
171         return -1;
172     }
173 }
174
175 static void * ffado_workthread(void *arg)
176 {
177     PRINT_FUNCTION_ENTRY;
178     snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)arg;
179
180     int oldstate;
181
182     pthread_setcancelstate (PTHREAD_CANCEL_DEFERRED, &oldstate);
183
184     while (1) {
185         snd_pcm_ffado_pollfunction(ffado);
186         pthread_testcancel();
187     }
188
189 }
190
191 static int snd_pcm_ffado_poll_revents(snd_pcm_ioplug_t *io,
192                      struct pollfd *pfds, unsigned int nfds,
193                      unsigned short *revents)
194 {
195     static char buf[1];
196     PRINT_FUNCTION_ENTRY;
197     assert(pfds && nfds == 1 && revents);
198     read(pfds[0].fd, buf, 1);
199     *revents = pfds[0].revents;
200     return 0;
201 }
202
203 static void snd_pcm_ffado_free(snd_pcm_ffado_t *ffado)
204 {
205     PRINT_FUNCTION_ENTRY;
206     if (ffado) {
207         close(ffado->fd);
208         close(ffado->io.poll_fd);
209         free(ffado);
210     }
211 }
212
213 static int snd_pcm_ffado_close(snd_pcm_ioplug_t *io)
214 {
215     PRINT_FUNCTION_ENTRY;
216     snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data;
217
218     // cleanup the SHM structures here
219     delete ffado->buffer;
220     ffado->buffer = NULL;
221
222     snd_pcm_ffado_free(ffado);
223     return 0;
224 }
225
226 static snd_pcm_sframes_t snd_pcm_ffado_pointer(snd_pcm_ioplug_t *io)
227 {
228     PRINT_FUNCTION_ENTRY;
229     snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data;
230     return ffado->hw_ptr;
231 }
232
233 static int snd_pcm_ffado_start(snd_pcm_ioplug_t *io)
234 {
235     int result = 0;
236     snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data;
237
238     PRINT_FUNCTION_ENTRY;
239
240     result = pthread_create (&ffado->thread, 0, ffado_workthread, ffado);
241     if(result) return result;
242
243     // FIXME: start the SHM stuff
244     return 0;
245 }
246
247 static int snd_pcm_ffado_stop(snd_pcm_ioplug_t *io)
248 {
249     snd_pcm_ffado_t *ffado = (snd_pcm_ffado_t *)io->private_data;
250
251     PRINT_FUNCTION_ENTRY;
252
253     if(pthread_cancel(ffado->thread)) {
254         debugError("could not cancel thread!\n");
255     }
256
257     if(pthread_join(ffado->thread,NULL)) {
258         debugError("could not join thread!\n");
259     }
260
261     // FIXME: stop the SHM client
262     return 0;
263 }
264
265 static int snd_pcm_ffado_prepare(snd_pcm_ioplug_t *io)
266 {
267     PRINT_FUNCTION_ENTRY;
268     return 0;
269 }
270
271 static snd_pcm_ioplug_callback_t ffado_pcm_callback;
272
273 #define ARRAY_SIZE(ary)    (sizeof(ary)/sizeof(ary[0]))
274
275 static int ffado_set_hw_constraint(snd_pcm_ffado_t *ffado)
276 {
277     PRINT_FUNCTION_ENTRY;
278     unsigned int access_list[] = {
279         SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
280         SND_PCM_ACCESS_RW_NONINTERLEAVED,
281     };
282
283     unsigned int rate_list[1];
284
285     unsigned int format = SND_PCM_FORMAT_S24;
286     int err;
287
288     // FIXME: make all of the parameters dynamic instead of static
289     rate_list[0] = 48000;
290
291     // setup the plugin capabilities
292     if ((err = snd_pcm_ioplug_set_param_list(&ffado->io, SND_PCM_IOPLUG_HW_ACCESS,
293                          ARRAY_SIZE(access_list), access_list)) < 0 ||
294         (err = snd_pcm_ioplug_set_param_list(&ffado->io, SND_PCM_IOPLUG_HW_RATE,
295                          ARRAY_SIZE(rate_list), rate_list)) < 0 ||
296         (err = snd_pcm_ioplug_set_param_list(&ffado->io, SND_PCM_IOPLUG_HW_FORMAT,
297                          1, &format)) < 0 ||
298         (err = snd_pcm_ioplug_set_param_minmax(&ffado->io, SND_PCM_IOPLUG_HW_CHANNELS,
299                            ffado->channels, ffado->channels)) < 0 ||
300         (err = snd_pcm_ioplug_set_param_minmax(&ffado->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
301                            ffado->period, ffado->period)) < 0 ||
302         (err = snd_pcm_ioplug_set_param_minmax(&ffado->io, SND_PCM_IOPLUG_HW_PERIODS,
303                            ffado->nb_buffers, ffado->nb_buffers)) < 0)
304         return err;
305
306     return 0;
307 }
308
309 static int snd_pcm_ffado_open(snd_pcm_t **pcmp, const char *name,
310                  snd_pcm_stream_t stream, int mode)
311 {
312
313     PRINT_FUNCTION_ENTRY;
314     snd_pcm_ffado_t *ffado;
315     int err;
316     int fd[2];
317
318     assert(pcmp);
319
320     ffado = (snd_pcm_ffado_t *)calloc(1, sizeof(*ffado));
321     if (!ffado)
322         return -ENOMEM;
323
324     ffado->stream=stream;
325
326     // discover the devices to discover the capabilities
327     // get the SHM structure
328
329     socketpair(AF_LOCAL, SOCK_STREAM, 0, fd);
330
331     ffado->fd = fd[0];
332
333     // initialize callback struct
334     ffado_pcm_callback.close = snd_pcm_ffado_close;
335     ffado_pcm_callback.start = snd_pcm_ffado_start;
336     ffado_pcm_callback.stop = snd_pcm_ffado_stop;
337     ffado_pcm_callback.pointer = snd_pcm_ffado_pointer;
338     ffado_pcm_callback.hw_params = snd_pcm_ffado_hw_params;
339     ffado_pcm_callback.prepare = snd_pcm_ffado_prepare;
340     ffado_pcm_callback.poll_revents = snd_pcm_ffado_poll_revents;
341
342     // prepare io struct
343     ffado->io.version = SND_PCM_IOPLUG_VERSION;
344     ffado->io.name = "FFADO PCM Plugin";
345     ffado->io.callback = &ffado_pcm_callback;
346     ffado->io.private_data = ffado;
347     ffado->io.mmap_rw = 1;
348     ffado->io.poll_fd = fd[1];
349     ffado->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
350
351     err = snd_pcm_ioplug_create(&ffado->io, name, stream, mode);
352     if (err < 0) {
353         snd_pcm_ffado_free(ffado);
354         return err;
355     }
356
357     err = ffado_set_hw_constraint(ffado);
358     if (err < 0) {
359         snd_pcm_ioplug_delete(&ffado->io);
360         return err;
361     }
362
363     *pcmp = ffado->io.pcm;
364
365     // these are the params
366     ffado->channels = 2;
367     ffado->period = 1024;
368     ffado->verbose = 6;
369     ffado->nb_buffers = 5;
370
371     setDebugLevel(ffado->verbose);
372
373     // prepare the IPC buffer
374     unsigned int buffsize = ffado->channels * ffado->period * 4;
375     if(stream == SND_PCM_STREAM_PLAYBACK) {
376         ffado->buffer = new IpcRingBuffer("playbackbuffer",
377                               IpcRingBuffer::eBT_Slave,
378                               IpcRingBuffer::eD_Outward,
379                               IpcRingBuffer::eB_Blocking,
380                               ffado->nb_buffers, buffsize);
381         if(ffado->buffer == NULL) {
382             debugError("Could not create playbackbuffer\n");
383             return -1;
384         }
385         if(!ffado->buffer->init()) {
386             debugError("Could not init playbackbuffer\n");
387             delete ffado->buffer;
388             ffado->buffer = NULL;
389             return -1;
390         }
391         ffado->buffer->setVerboseLevel(ffado->verbose);
392     } else {
393         ffado->buffer = new IpcRingBuffer("capturebuffer",
394                               IpcRingBuffer::eBT_Slave,
395                               IpcRingBuffer::eD_Inward,
396                               IpcRingBuffer::eB_Blocking,
397                               ffado->nb_buffers, buffsize);
398         if(ffado->buffer == NULL) {
399             debugError("Could not create capturebuffer\n");
400             return -1;
401         }
402         if(!ffado->buffer->init()) {
403             debugError("Could not init capturebuffer\n");
404             delete ffado->buffer;
405             ffado->buffer = NULL;
406             return -1;
407         }
408         ffado->buffer->setVerboseLevel(ffado->verbose);
409     }
410
411     return 0;
412 }
413
414 SND_PCM_PLUGIN_DEFINE_FUNC(ffado)
415 {
416     printMessage("FireWire plugin for ALSA\n  version %s compiled %s %s\n  using %s\n",
417         FFADO_PLUGIN_VERSION, __DATE__, __TIME__, PACKAGE_STRING);
418
419     snd_config_iterator_t i, next;
420     int err;
421
422     snd_config_for_each(i, next, conf) {
423         snd_config_t *n = snd_config_iterator_entry(i);
424         const char *id;
425         if (snd_config_get_id(n, &id) < 0)
426             continue;
427         if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0)
428             continue;
429
430         SNDERR("Unknown field %s", id);
431         return -EINVAL;
432     }
433
434     err = snd_pcm_ffado_open(pcmp, name, stream, mode);
435
436     return err;
437
438 }
439
440 SND_PCM_PLUGIN_SYMBOL(ffado);
441
442 } // extern "C"
Note: See TracBrowser for help on using the browser.