root/branches/ppalmers-streaming/IsoHandlerManager.cpp

Revision 494, 21.1 kB (checked in by ppalmers, 17 years ago)

- switch over to a generic ffado_timestamp_t for the timestamped buffer (currently float)
- implemented some experimental stream phase sync method

- various small things

NOTE: not a very stable commit

Line 
1 /*
2  * Copyright (C) 2005-2007 by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License version 2.1, as published by the Free Software Foundation;
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21  * MA 02110-1301 USA
22  */
23
24 #include "IsoHandlerManager.h"
25 #include "IsoHandler.h"
26 #include "IsoStream.h"
27 #include <assert.h>
28
29 #include "../libutil/PosixThread.h"
30
31
32 #define MINIMUM_INTERRUPTS_PER_PERIOD  2U
33 #define PACKETS_PER_INTERRUPT          4U
34
35 namespace Streaming
36 {
37
38 IMPL_DEBUG_MODULE( IsoHandlerManager, IsoHandlerManager, DEBUG_LEVEL_NORMAL );
39
40 IsoHandlerManager::IsoHandlerManager() :
41    m_State(E_Created),
42    m_poll_timeout(100), m_poll_fds(0), m_poll_nfds(0),
43    m_realtime(false), m_priority(0)
44 {
45
46 }
47
48 IsoHandlerManager::IsoHandlerManager(bool run_rt, unsigned int rt_prio) :
49    m_State(E_Created),
50    m_poll_timeout(1), m_poll_fds(0), m_poll_nfds(0),
51    m_realtime(run_rt), m_priority(rt_prio)
52 {
53
54 }
55
56 IsoHandlerManager::~IsoHandlerManager()
57 {
58
59 }
60
61 bool IsoHandlerManager::init()
62 {
63     // the tread that performs the actual packet transfer
64     // needs high priority
65     unsigned int prio=m_priority+6;
66
67     if (prio>98) prio=98;
68
69     m_isoManagerThread=new Util::PosixThread(
70         this,
71         m_realtime, prio,
72         PTHREAD_CANCEL_DEFERRED);
73
74     if(!m_isoManagerThread) {
75         debugFatal("Could not create iso manager thread\n");
76         return false;
77     }
78
79     // propagate the debug level
80 //     m_isoManagerThread->setVerboseLevel(getDebugLevel());
81
82     return true;
83 }
84
85 bool IsoHandlerManager::Init()
86 {
87     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
88
89     return true;
90 }
91
92 /**
93  * the IsoHandlerManager thread execute function iterates the handlers.
94  *
95  * This means that once the thread is running, streams are
96  * transmitted and received (if present on the bus). Make sure
97  * that the clients are registered & ready before starting the
98  * thread!
99  *
100  * The register and unregister functions are thread unsafe, so
101  * should not be used when the thread is running.
102  *
103  * @return false if the handlers could not be iterated.
104  */
105 bool IsoHandlerManager::Execute()
106 {
107 //     updateCycleTimers();
108
109     if(!iterate()) {
110         debugFatal("Could not iterate the isoManager\n");
111         return false;
112     }
113
114     return true;
115 }
116
117 /**
118  * Poll the handlers managed by this manager, and iterate them
119  * when ready
120  *
121  * @return true when successful
122  */
123 bool IsoHandlerManager::iterate()
124 {
125     int err;
126     int i=0;
127     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "enter...\n");
128
129     err = poll (m_poll_fds, m_poll_nfds, m_poll_timeout);
130
131     if (err == -1) {
132         if (errno == EINTR) {
133             return true;
134         }
135         debugFatal("poll error: %s\n", strerror (errno));
136         return false;
137     }
138
139     for (i = 0; i < m_poll_nfds; i++) {
140         if (m_poll_fds[i].revents & POLLERR) {
141             debugWarning("error on fd for %d\n",i);
142         }
143
144         if (m_poll_fds[i].revents & POLLHUP) {
145             debugWarning("hangup on fd for %d\n",i);
146         }
147
148         if(m_poll_fds[i].revents & (POLLIN)) {
149             IsoHandler *s=m_IsoHandlers.at(i);
150             assert(s);
151
152             s->iterate();
153         }
154     }
155
156     return true;
157
158 }
159
160 bool IsoHandlerManager::registerHandler(IsoHandler *handler)
161 {
162     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
163     assert(handler);
164
165     m_IsoHandlers.push_back(handler);
166
167     handler->setVerboseLevel(getDebugLevel());
168
169     // rebuild the fd map for poll()'ing.
170     return rebuildFdMap();
171
172 }
173
174 bool IsoHandlerManager::unregisterHandler(IsoHandler *handler)
175 {
176     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
177     assert(handler);
178
179     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
180       it != m_IsoHandlers.end();
181       ++it )
182     {
183         if ( *it == handler ) {
184             // erase the iso handler from the list
185             m_IsoHandlers.erase(it);
186             // rebuild the fd map for poll()'ing.
187             return rebuildFdMap();
188         }
189     }
190     debugFatal("Could not find handler (%p)\n", handler);
191
192     return false; //not found
193
194 }
195
196 bool IsoHandlerManager::rebuildFdMap() {
197     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
198     int i=0;
199
200     m_poll_nfds=0;
201     if(m_poll_fds) free(m_poll_fds);
202
203     // count the number of handlers
204     m_poll_nfds=m_IsoHandlers.size();
205
206     // allocate the fd array
207     m_poll_fds   = (struct pollfd *) calloc (m_poll_nfds, sizeof (struct pollfd));
208     if(!m_poll_fds) {
209         debugFatal("Could not allocate memory for poll FD array\n");
210         return false;
211     }
212
213     // fill the fd map
214     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
215       it != m_IsoHandlers.end();
216       ++it )
217     {
218         m_poll_fds[i].fd=(*it)->getFileDescriptor();
219         m_poll_fds[i].events = POLLIN;
220         i++;
221     }
222
223     return true;
224 }
225
226 void IsoHandlerManager::disablePolling(IsoStream *stream) {
227     int i=0;
228
229     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Disable polling on stream %p\n",stream);
230
231     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
232         it != m_IsoHandlers.end();
233         ++it )
234     {
235         if ((*it)->isStreamRegistered(stream)) {
236             m_poll_fds[i].events = 0;
237             m_poll_fds[i].revents = 0;
238             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "polling disabled\n");
239         }
240
241         i++;
242     }
243 }
244
245 void IsoHandlerManager::enablePolling(IsoStream *stream) {
246     int i=0;
247
248     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Enable polling on stream %p\n",stream);
249
250     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
251         it != m_IsoHandlers.end();
252         ++it )
253     {
254         if ((*it)->isStreamRegistered(stream)) {
255             m_poll_fds[i].events = POLLIN;
256             m_poll_fds[i].revents = 0;
257             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "polling enabled\n");
258         }
259
260         i++;
261     }
262 }
263
264
265 /**
266  * Registers an IsoStream with the IsoHandlerManager.
267  *
268  * If nescessary, an IsoHandler is created to handle this stream.
269  * Once an IsoStream is registered to the handler, it will be included
270  * in the ISO streaming cycle (i.e. receive/transmit of it will occur).
271  *
272  * @param stream the stream to register
273  * @return true if registration succeeds
274  *
275  * \todo : currently there is a one-to-one mapping
276  *        between streams and handlers, this is not ok for
277  *        multichannel receive
278  */
279 bool IsoHandlerManager::registerStream(IsoStream *stream)
280 {
281     debugOutput( DEBUG_LEVEL_VERBOSE, "Registering stream %p\n",stream);
282     assert(stream);
283
284     // make sure the stream isn't already attached to a handler
285     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
286       it != m_IsoHandlers.end();
287       ++it )
288     {
289         if((*it)->isStreamRegistered(stream)) {
290             debugWarning( "stream already registered!\n");
291             (*it)->unregisterStream(stream);
292
293         }
294     }
295
296     // clean up all handlers that aren't used
297     pruneHandlers();
298
299     // allocate a handler for this stream
300     if (stream->getType()==IsoStream::EST_Receive) {
301         // setup the optimal parameters for the raw1394 ISO buffering
302         unsigned int packets_per_period=stream->getPacketsPerPeriod();
303
304 #if 1
305         // hardware interrupts occur when one DMA block is full, and the size of one DMA
306         // block = PAGE_SIZE. Setting the max_packet_size makes sure that the HW irq
307         // occurs at a period boundary (optimal CPU use)
308
309         // NOTE: try and use MINIMUM_INTERRUPTS_PER_PERIOD hardware interrupts
310         //       per period for better latency.
311         unsigned int max_packet_size=(MINIMUM_INTERRUPTS_PER_PERIOD * getpagesize()) / packets_per_period;
312         if (max_packet_size < stream->getMaxPacketSize()) {
313             max_packet_size=stream->getMaxPacketSize();
314         }
315
316         // Ensure we don't request a packet size bigger than the
317         // kernel-enforced maximum which is currently 1 page.
318         if (max_packet_size > (unsigned int)getpagesize())
319                     max_packet_size = getpagesize();
320
321         unsigned int irq_interval=packets_per_period / MINIMUM_INTERRUPTS_PER_PERIOD;
322         if(irq_interval <= 0) irq_interval=1;
323 #else
324         // hardware interrupts occur when one DMA block is full, and the size of one DMA
325         // block = PAGE_SIZE. Setting the max_packet_size enables control over the IRQ
326         // frequency, as the controller uses max_packet_size, and not the effective size
327         // when writing to the DMA buffer.
328
329         // configure it such that we have an irq for every PACKETS_PER_INTERRUPT packets
330         unsigned int irq_interval=PACKETS_PER_INTERRUPT;
331
332         // unless the period size doesn't allow this
333         if ((packets_per_period/MINIMUM_INTERRUPTS_PER_PERIOD) < irq_interval) {
334             irq_interval=1;
335         }
336
337         // FIXME: test
338         irq_interval=1;
339 #warning Using fixed irq_interval
340
341         unsigned int max_packet_size=getpagesize() / irq_interval;
342
343         if (max_packet_size < stream->getMaxPacketSize()) {
344             max_packet_size=stream->getMaxPacketSize();
345         }
346
347         // Ensure we don't request a packet size bigger than the
348         // kernel-enforced maximum which is currently 1 page.
349         if (max_packet_size > (unsigned int)getpagesize())
350                     max_packet_size = getpagesize();
351
352 #endif
353         /* the receive buffer size doesn't matter for the latency,
354            but it has a minimal value in order for libraw to operate correctly (300) */
355         int buffers=400;
356
357         // create the actual handler
358         IsoRecvHandler *h = new IsoRecvHandler(stream->getPort(), buffers,
359                                                max_packet_size, irq_interval);
360
361         debugOutput( DEBUG_LEVEL_VERBOSE, " registering IsoRecvHandler\n");
362
363         if(!h) {
364             debugFatal("Could not create IsoRecvHandler\n");
365             return false;
366         }
367
368         h->setVerboseLevel(getDebugLevel());
369
370         // init the handler
371         if(!h->init()) {
372             debugFatal("Could not initialize receive handler\n");
373             return false;
374         }
375
376         // register the stream with the handler
377         if(!h->registerStream(stream)) {
378             debugFatal("Could not register receive stream with handler\n");
379             return false;
380         }
381
382         // register the handler with the manager
383         if(!registerHandler(h)) {
384             debugFatal("Could not register receive handler with manager\n");
385             return false;
386         }
387         debugOutput( DEBUG_LEVEL_VERBOSE, " registered stream (%p) with handler (%p)\n",stream,h);
388     }
389
390     if (stream->getType()==IsoStream::EST_Transmit) {
391         // setup the optimal parameters for the raw1394 ISO buffering
392         unsigned int packets_per_period=stream->getPacketsPerPeriod();
393
394 #if 1
395         // hardware interrupts occur when one DMA block is full, and the size of one DMA
396         // block = PAGE_SIZE. Setting the max_packet_size makes sure that the HW irq
397         // occurs at a period boundary (optimal CPU use)
398         // NOTE: try and use MINIMUM_INTERRUPTS_PER_PERIOD interrupts per period
399         //       for better latency.
400         unsigned int max_packet_size=MINIMUM_INTERRUPTS_PER_PERIOD * getpagesize() / packets_per_period;
401         if (max_packet_size < stream->getMaxPacketSize()) {
402             max_packet_size=stream->getMaxPacketSize();
403         }
404
405         // Ensure we don't request a packet size bigger than the
406         // kernel-enforced maximum which is currently 1 page.
407         if (max_packet_size > (unsigned int)getpagesize())
408                     max_packet_size = getpagesize();
409
410          unsigned int irq_interval=packets_per_period / MINIMUM_INTERRUPTS_PER_PERIOD;
411          if(irq_interval <= 0) irq_interval=1;
412 #else
413         // hardware interrupts occur when one DMA block is full, and the size of one DMA
414         // block = PAGE_SIZE. Setting the max_packet_size enables control over the IRQ
415         // frequency, as the controller uses max_packet_size, and not the effective size
416         // when writing to the DMA buffer.
417
418         // configure it such that we have an irq for every PACKETS_PER_INTERRUPT packets
419         unsigned int irq_interval=PACKETS_PER_INTERRUPT;
420
421         // unless the period size doesn't allow this
422         if ((packets_per_period/MINIMUM_INTERRUPTS_PER_PERIOD) < irq_interval) {
423             irq_interval=1;
424         }
425
426         // FIXME: test
427         irq_interval=1;
428 #warning Using fixed irq_interval
429
430         unsigned int max_packet_size=getpagesize() / irq_interval;
431
432         if (max_packet_size < stream->getMaxPacketSize()) {
433             max_packet_size=stream->getMaxPacketSize();
434         }
435
436         // Ensure we don't request a packet size bigger than the
437         // kernel-enforced maximum which is currently 1 page.
438         if (max_packet_size > (unsigned int)getpagesize())
439                     max_packet_size = getpagesize();
440 #endif
441         // the transmit buffer size should be as low as possible for latency.
442         // note however that the raw1394 subsystem tries to keep this buffer
443         // full, so we have to make sure that we have enough events in our
444         // event buffers
445
446         // every irq_interval packets an interrupt will occur. that is when
447         // buffers get transfered, meaning that we should have at least some
448         // margin here
449         int buffers=irq_interval * 2;
450
451         // half a period. the xmit handler will take care of this
452 //         int buffers=packets_per_period/4;
453
454         // NOTE: this is dangerous: what if there is not enough prefill?
455 //         if (buffers<10) buffers=10;
456
457         // create the actual handler
458         IsoXmitHandler *h = new IsoXmitHandler(stream->getPort(), buffers,
459                                                max_packet_size, irq_interval);
460
461         debugOutput( DEBUG_LEVEL_VERBOSE, " registering IsoXmitHandler\n");
462
463         if(!h) {
464             debugFatal("Could not create IsoXmitHandler\n");
465             return false;
466         }
467
468         h->setVerboseLevel(getDebugLevel());
469
470         // init the handler
471         if(!h->init()) {
472             debugFatal("Could not initialize transmit handler\n");
473             return false;
474         }
475
476         // register the stream with the handler
477         if(!h->registerStream(stream)) {
478             debugFatal("Could not register transmit stream with handler\n");
479             return false;
480         }
481
482         // register the handler with the manager
483         if(!registerHandler(h)) {
484             debugFatal("Could not register transmit handler with manager\n");
485             return false;
486         }
487         debugOutput( DEBUG_LEVEL_VERBOSE, " registered stream (%p) with handler (%p)\n",stream,h);
488     }
489
490     m_IsoStreams.push_back(stream);
491     debugOutput( DEBUG_LEVEL_VERBOSE, " %d streams, %d handlers registered\n",
492                                       m_IsoStreams.size(), m_IsoHandlers.size());
493
494     return true;
495 }
496
497 bool IsoHandlerManager::unregisterStream(IsoStream *stream)
498 {
499     debugOutput( DEBUG_LEVEL_VERBOSE, "Unregistering stream %p\n",stream);
500     assert(stream);
501
502     // make sure the stream isn't attached to a handler anymore
503     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
504       it != m_IsoHandlers.end();
505       ++it )
506     {
507         if((*it)->isStreamRegistered(stream)) {
508             if(!(*it)->unregisterStream(stream)) {
509                 debugOutput( DEBUG_LEVEL_VERBOSE, " could not unregister stream (%p) from handler (%p)...\n",stream,*it);
510                 return false;
511             }
512
513             debugOutput( DEBUG_LEVEL_VERBOSE, " unregistered stream (%p) from handler (%p)...\n",stream,*it);
514         }
515     }
516
517     // clean up all handlers that aren't used
518     pruneHandlers();
519
520     // remove the stream from the registered streams list
521     for ( IsoStreamVectorIterator it = m_IsoStreams.begin();
522       it != m_IsoStreams.end();
523       ++it )
524     {
525         if ( *it == stream ) {
526             m_IsoStreams.erase(it);
527
528             debugOutput( DEBUG_LEVEL_VERBOSE, " deleted stream (%p) from list...\n", *it);
529             return true;
530         }
531     }
532
533     return false; //not found
534
535 }
536
537 void IsoHandlerManager::pruneHandlers() {
538     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
539     IsoHandlerVector toUnregister;
540
541     // find all handlers that are not in use
542     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
543           it != m_IsoHandlers.end();
544           ++it )
545     {
546         if(!((*it)->inUse())) {
547             debugOutput( DEBUG_LEVEL_VERBOSE, " handler (%p) not in use\n",*it);
548             toUnregister.push_back(*it);
549         }
550     }
551     // delete them
552     for ( IsoHandlerVectorIterator it = toUnregister.begin();
553           it != toUnregister.end();
554           ++it )
555     {
556         unregisterHandler(*it);
557         debugOutput( DEBUG_LEVEL_VERBOSE, " deleting handler (%p)\n",*it);
558
559         // Now the handler's been unregistered it won't be reused
560         // again.  Therefore it really needs to be formally deleted
561         // to free up the raw1394 handle.  Otherwise things fall
562         // apart after several xrun recoveries as the system runs
563         // out of resources to support all the disused but still
564         // allocated raw1394 handles.  At least this is the current
565         // theory as to why we end up with "memory allocation"
566         // failures after several Xrun recoveries.
567         delete *it;
568     }
569
570 }
571
572
573 bool IsoHandlerManager::prepare()
574 {
575     bool retval=true;
576
577     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
578
579     // check state
580     if(m_State != E_Created) {
581         debugError("Incorrect state, expected E_Created, got %d\n",(int)m_State);
582         return false;
583     }
584
585     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
586           it != m_IsoHandlers.end();
587           ++it )
588     {
589         if(!(*it)->prepare()) {
590             debugFatal("Could not prepare handlers\n");
591             retval=false;
592         }
593     }
594
595     if (retval) {
596         m_State=E_Prepared;
597     } else {
598         m_State=E_Error;
599     }
600
601     return retval;
602 }
603
604 bool IsoHandlerManager::startHandlers() {
605     return startHandlers(-1);
606 }
607
608 bool IsoHandlerManager::startHandlers(int cycle) {
609     bool retval=true;
610
611     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
612
613     // check state
614     if(m_State != E_Prepared) {
615         debugError("Incorrect state, expected E_Prepared, got %d\n",(int)m_State);
616         return false;
617     }
618
619     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
620         it != m_IsoHandlers.end();
621         ++it )
622     {
623         debugOutput( DEBUG_LEVEL_VERBOSE, " starting handler (%p)\n",*it);
624         if(!(*it)->start(cycle)) {
625             debugOutput( DEBUG_LEVEL_VERBOSE, " could not start handler (%p)\n",*it);
626             retval=false;
627         }
628     }
629
630     debugOutput( DEBUG_LEVEL_VERBOSE, "Starting ISO iterator thread...\n");
631
632     // note: libraw1394 doesn't like it if you poll() and/or iterate() before
633     //       starting the streams.
634     // start the iso runner thread
635     m_isoManagerThread->Start();
636
637     if (retval) {
638         m_State=E_Running;
639     } else {
640         m_State=E_Error;
641     }
642
643     return retval;
644 }
645
646 bool IsoHandlerManager::stopHandlers() {
647     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
648
649     // check state
650     if(m_State != E_Running) {
651         debugError("Incorrect state, expected E_Running, got %d\n",(int)m_State);
652         return false;
653     }
654
655     bool retval=true;
656
657     debugOutput( DEBUG_LEVEL_VERBOSE, "Stopping ISO iterator thread...\n");
658     m_isoManagerThread->Stop();
659
660     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
661         it != m_IsoHandlers.end();
662         ++it )
663     {
664         debugOutput( DEBUG_LEVEL_VERBOSE, "Stopping handler (%p)\n",*it);
665         if(!(*it)->stop()){
666             debugOutput( DEBUG_LEVEL_VERBOSE, " could not stop handler (%p)\n",*it);
667             retval=false;
668         }
669     }
670
671     if (retval) {
672         m_State=E_Prepared;
673     } else {
674         m_State=E_Error;
675     }
676
677     return retval;
678 }
679
680 bool IsoHandlerManager::reset() {
681     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
682
683     // check state
684     if(m_State == E_Error) {
685         debugFatal("Resetting from error condition not yet supported...\n");
686         return false;
687     }
688
689     // if not in an error condition, reset means stop the handlers
690     return stopHandlers();
691 }
692
693
694 void IsoHandlerManager::setVerboseLevel(int i) {
695     setDebugLevel(i);
696
697     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
698           it != m_IsoHandlers.end();
699           ++it )
700     {
701         (*it)->setVerboseLevel(i);
702     }
703 }
704
705 void IsoHandlerManager::dumpInfo() {
706     int i=0;
707
708     debugOutputShort( DEBUG_LEVEL_NORMAL, "Dumping IsoHandlerManager Stream handler information...\n");
709     debugOutputShort( DEBUG_LEVEL_NORMAL, " State: %d\n",(int)m_State);
710
711     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
712           it != m_IsoHandlers.end();
713           ++it )
714     {
715         debugOutputShort( DEBUG_LEVEL_NORMAL, " IsoHandler %d (%p)\n",i++,*it);
716
717         (*it)->dumpInfo();
718     }
719
720 }
721
722 } // end of namespace Streaming
723
Note: See TracBrowser for help on using the browser.