root/trunk/libfreebob/src/libstreaming/IsoHandlerManager.cpp

Revision 360, 14.4 kB (checked in by pieterpalmers, 16 years ago)

- temporary commit to backup some work

- Started a framework to synchronize IsoHandlers? to

any generic TimeSource?. The idea is to introduce
one overall time reference, and resynchronize all
other timed events to this time source.
This will, on the long run, allow:

  • combining devices on multiple FW busses together,
    as these are not synched by hardware.
  • synchronizing to the system clock
  • synchronizing to any other time source (e.g.
    when implementing a jackd client, i.e. using
    the freebob devices as jackd clients).

- Implemented a realtime safe way to read the cycle

timer for an IsoHandler?. (+ test application)

- Implemented tests/test-sytmonitor:

Monitors 2 or more channels and reports the average
SYT timestamp difference between both.

- Messed around with SYT timestamping for AMDTP. Doesn't

work (yet).

Line 
1 /* $Id$ */
2
3 /*
4  *   FreeBob Streaming API
5  *   FreeBob = Firewire (pro-)audio for linux
6  *
7  *   http://freebob.sf.net
8  *
9  *   Copyright (C) 2006 Pieter Palmers <pieterpalmers@users.sourceforge.net>
10  *
11  *   This program is free software {} you can redistribute it and/or modify
12  *   it under the terms of the GNU General Public License as published by
13  *   the Free Software Foundation {} either version 2 of the License, or
14  *   (at your option) any later version.
15  *
16  *   This program is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY {} without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  *
21  *   You should have received a copy of the GNU General Public License
22  *   along with this program {} if not, write to the Free Software
23  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  *
25  *
26  *
27  */
28
29 #include "IsoHandlerManager.h"
30 #include "IsoHandler.h"
31 #include "IsoStream.h"
32 #include <assert.h>
33
34
35 namespace FreebobStreaming
36 {
37
38 IMPL_DEBUG_MODULE( IsoHandlerManager, IsoHandlerManager, DEBUG_LEVEL_NORMAL );
39
40 IsoHandlerManager::IsoHandlerManager() :
41    m_poll_timeout(100), m_poll_fds(0), m_poll_nfds(0)
42 {
43
44 }
45
46
47 IsoHandlerManager::~IsoHandlerManager()
48 {
49
50 }
51
52 bool IsoHandlerManager::Init()
53 {
54         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
55
56         return true;
57 }
58
59 // the IsoHandlerManager thread updates the handler caches
60 // it doesn't iterate them !!!
61 bool IsoHandlerManager::Execute()
62 {
63     updateCycleCounters();
64     usleep(USLEEP_AFTER_UPDATE);
65    
66     return true;
67 }
68
69 bool IsoHandlerManager::iterate()
70 {
71         int err;
72         int i=0;
73         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "enter...\n");
74        
75         err = poll (m_poll_fds, m_poll_nfds, m_poll_timeout);
76        
77         if (err == -1) {
78                 if (errno == EINTR) {
79                         return true;
80                 }
81                 debugFatal("poll error: %s\n", strerror (errno));
82                 return false;
83         }
84
85         for (i = 0; i < m_poll_nfds; i++) {
86                 if (m_poll_fds[i].revents & POLLERR) {
87                         debugWarning("error on fd for %d\n",i);
88                 }
89
90                 if (m_poll_fds[i].revents & POLLHUP) {
91                         debugWarning("hangup on fd for %d\n",i);
92                 }
93                
94                 if(m_poll_fds[i].revents & (POLLIN)) {
95                         IsoHandler *s=m_IsoHandlers.at(i);
96                         assert(s);
97                        
98                         s->iterate();
99                 }
100         }
101
102         return true;
103
104 }
105
106 // updates the internal cycle counter caches of the handlers
107 void IsoHandlerManager::updateCycleCounters() {
108         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "enter...\n");
109        
110     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
111           it != m_IsoHandlers.end();
112           ++it )
113     {
114         int cnt=0;
115         while (!(*it)->updateCycleCounter() && (cnt++ < MAX_UPDATE_TRIES)) {
116             usleep(USLEEP_AFTER_UPDATE_FAILURE);
117         }
118     }
119    
120 }
121
122 bool IsoHandlerManager::prepare()
123 {
124         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
125     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
126           it != m_IsoHandlers.end();
127           ++it )
128     {
129         if(!(*it)->prepare()) {
130                         debugFatal("Could not prepare handlers\n");
131                         return false;
132         }
133     }
134
135         return true;
136 }
137
138
139
140 bool IsoHandlerManager::registerHandler(IsoHandler *handler)
141 {
142         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
143         assert(handler);
144        
145         m_IsoHandlers.push_back(handler);
146        
147         handler->setVerboseLevel(getDebugLevel());
148
149         // rebuild the fd map for poll()'ing.
150         return rebuildFdMap(); 
151
152 }
153
154 bool IsoHandlerManager::unregisterHandler(IsoHandler *handler)
155 {
156         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
157         assert(handler);
158
159         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
160           it != m_IsoHandlers.end();
161           ++it )
162         {
163                 if ( *it == handler ) {
164                         // erase the iso handler from the list
165                         m_IsoHandlers.erase(it);
166                         // rebuild the fd map for poll()'ing.
167                         return rebuildFdMap();
168                 }
169         }
170         debugFatal("Could not find handler (%p)\n", handler);
171        
172         return false; //not found
173
174 }
175
176 bool IsoHandlerManager::rebuildFdMap() {
177         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
178         int i=0;
179
180         m_poll_nfds=0;
181         if(m_poll_fds) free(m_poll_fds);
182
183         // count the number of handlers
184         m_poll_nfds=m_IsoHandlers.size();
185
186         // allocate the fd array
187         m_poll_fds   = (struct pollfd *) calloc (m_poll_nfds, sizeof (struct pollfd));
188         if(!m_poll_fds) {
189                 debugFatal("Could not allocate memory for poll FD array\n");
190                 return false;
191         }
192
193         // fill the fd map
194         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
195           it != m_IsoHandlers.end();
196           ++it )
197         {
198                 m_poll_fds[i].fd=(*it)->getFileDescriptor();
199                 m_poll_fds[i].events = POLLIN;
200                 i++;
201         }
202
203         return true;
204 }
205
206 void IsoHandlerManager::disablePolling(IsoStream *stream) {
207     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Disable polling on stream %p\n",stream);
208         int i=0;
209         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
210           it != m_IsoHandlers.end();
211           ++it )
212         {
213            if ((*it)->isStreamRegistered(stream)) {
214                m_poll_fds[i].events = 0;
215                m_poll_fds[i].revents = 0;
216             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "polling disabled\n");
217            }
218            i++;
219         }
220
221 }
222
223 void IsoHandlerManager::enablePolling(IsoStream *stream) {
224     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Enable polling on stream %p\n",stream);
225         int i=0;
226         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
227           it != m_IsoHandlers.end();
228           ++it )
229         {
230            if ((*it)->isStreamRegistered(stream)) {
231                m_poll_fds[i].events = POLLIN;
232                m_poll_fds[i].revents = 0;
233             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "polling enabled\n");
234            }
235            i++;
236         }
237 }
238
239
240 /**
241  * Registers an IsoStream with the IsoHandlerManager.
242  *
243  * If nescessary, an IsoHandler is created to handle this stream.
244  * Once an IsoStream is registered to the handler, it will be included
245  * in the ISO streaming cycle (i.e. receive/transmit of it will occur).
246  *
247  * @param stream the stream to register
248  * @return true if registration succeeds
249  *
250  * \todo : currently there is a one-to-one mapping
251  *        between streams and handlers, this is not ok for
252  *        multichannel receive
253  */
254 bool IsoHandlerManager::registerStream(IsoStream *stream)
255 {
256         debugOutput( DEBUG_LEVEL_VERBOSE, "Registering stream %p\n",stream);
257         assert(stream);
258
259         // make sure the stream isn't already attached to a handler
260         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
261           it != m_IsoHandlers.end();
262           ++it )
263         {
264                 if((*it)->isStreamRegistered(stream)) {
265                         debugWarning( "stream already registered!\n");
266                         (*it)->unregisterStream(stream);
267                        
268                 }
269         }
270        
271         // clean up all handlers that aren't used
272         pruneHandlers();
273
274         // allocate a handler for this stream
275         if (stream->getType()==IsoStream::EST_Receive) {
276                 // setup the optimal parameters for the raw1394 ISO buffering
277                 unsigned int packets_per_period=stream->getPacketsPerPeriod();
278                
279                 // hardware interrupts occur when one DMA block is full, and the size of one DMA
280                 // block = PAGE_SIZE. Setting the max_packet_size makes sure that the HW irq is
281                 // occurs at a period boundary (optimal CPU use)
282                
283                 // NOTE: try and use 4 hardware interrupts per period for better latency.
284                 unsigned int max_packet_size=4 * getpagesize() / packets_per_period;
285                 if (max_packet_size < stream->getMaxPacketSize()) {
286                         max_packet_size=stream->getMaxPacketSize();
287                 }
288
289                 // Ensure we don't request a packet size bigger than the
290                 // kernel-enforced maximum which is currently 1 page.
291                 if (max_packet_size > (unsigned int)getpagesize())
292                         max_packet_size = getpagesize();
293
294                 int irq_interval=packets_per_period / 4;
295         if(irq_interval <= 0) irq_interval=1;
296
297                 /* the receive buffer size doesn't matter for the latency,
298                    but it has a minimal value in order for libraw to operate correctly (300) */
299                 int buffers=400;
300                
301                 // create the actual handler
302                 IsoRecvHandler *h = new IsoRecvHandler(stream->getPort(), buffers,
303                                                        max_packet_size, irq_interval);
304
305                 debugOutput( DEBUG_LEVEL_VERBOSE, " registering IsoRecvHandler\n");
306
307                 if(!h) {
308                         debugFatal("Could not create IsoRecvHandler\n");
309                         return false;
310                 }
311
312                 h->setVerboseLevel(getDebugLevel());
313
314                 // init the handler
315                 if(!h->init()) {
316                         debugFatal("Could not initialize receive handler\n");
317                         return false;
318                 }
319
320                 // register the stream with the handler
321                 if(!h->registerStream(stream)) {
322                         debugFatal("Could not register receive stream with handler\n");
323                         return false;
324                 }
325
326                 // register the handler with the manager
327                 if(!registerHandler(h)) {
328                         debugFatal("Could not register receive handler with manager\n");
329                         return false;
330                 }
331                 debugOutput( DEBUG_LEVEL_VERBOSE, " registered stream (%p) with handler (%p)\n",stream,h);
332         }
333        
334         if (stream->getType()==IsoStream::EST_Transmit) {
335        
336                 // setup the optimal parameters for the raw1394 ISO buffering
337                 unsigned int packets_per_period=stream->getPacketsPerPeriod();
338                 // hardware interrupts occur when one DMA block is full, and the size of one DMA
339                 // block = PAGE_SIZE. Setting the max_packet_size makes sure that the HW irq 
340                 // occurs at a period boundary (optimal CPU use)
341                 // NOTE: try and use 4 interrupts per period for better latency.
342                 unsigned int max_packet_size=4 * getpagesize() / packets_per_period;
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                 int irq_interval=packets_per_period / 4;
353                 if(irq_interval <= 0) irq_interval=1;
354        
355                 // the transmit buffer size should be as low as possible for latency.
356                 // note however that the raw1394 subsystem tries to keep this buffer
357                 // full, so we have to make sure that we have enough events in our
358                 // event buffers
359                 int buffers=packets_per_period;
360                
361                 // NOTE: this is dangerous: what if there is not enough prefill?
362 //              if (buffers<10) buffers=10;     
363                
364                 // create the actual handler
365                 IsoXmitHandler *h = new IsoXmitHandler(stream->getPort(), buffers,
366                                                        max_packet_size, irq_interval);
367
368                 debugOutput( DEBUG_LEVEL_VERBOSE, " registering IsoXmitHandler\n");
369
370                 if(!h) {
371                         debugFatal("Could not create IsoXmitHandler\n");
372                         return false;
373                 }
374
375                 h->setVerboseLevel(getDebugLevel());
376
377                 // init the handler
378                 if(!h->init()) {
379                         debugFatal("Could not initialize transmit handler\n");
380                         return false;
381                 }
382
383                 // register the stream with the handler
384                 if(!h->registerStream(stream)) {
385                         debugFatal("Could not register transmit stream with handler\n");
386                         return false;
387                 }
388
389                 // register the handler with the manager
390                 if(!registerHandler(h)) {
391                         debugFatal("Could not register transmit handler with manager\n");
392                         return false;
393                 }
394                 debugOutput( DEBUG_LEVEL_VERBOSE, " registered stream (%p) with handler (%p)\n",stream,h);
395
396         }
397
398         m_IsoStreams.push_back(stream);
399         debugOutput( DEBUG_LEVEL_VERBOSE, " %d streams, %d handlers registered\n",
400                                           m_IsoStreams.size(), m_IsoHandlers.size());
401
402         return true;
403 }
404
405 bool IsoHandlerManager::unregisterStream(IsoStream *stream)
406 {
407         debugOutput( DEBUG_LEVEL_VERBOSE, "Unregistering stream %p\n",stream);
408         assert(stream);
409
410         // make sure the stream isn't attached to a handler anymore
411         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
412           it != m_IsoHandlers.end();
413           ++it )
414         {
415                 if((*it)->isStreamRegistered(stream)) {
416                         if(!(*it)->unregisterStream(stream)) {
417                                 debugOutput( DEBUG_LEVEL_VERBOSE, " could not unregister stream (%p) from handler (%p)...\n",stream,*it);
418                                 return false;
419                         }
420                        
421                         debugOutput( DEBUG_LEVEL_VERBOSE, " unregistered stream (%p) from handler (%p)...\n",stream,*it);
422                 }
423         }
424
425         // clean up all handlers that aren't used
426         pruneHandlers();
427
428         // remove the stream from the registered streams list
429         for ( IsoStreamVectorIterator it = m_IsoStreams.begin();
430           it != m_IsoStreams.end();
431           ++it )
432         {
433                 if ( *it == stream ) {
434                         m_IsoStreams.erase(it);
435                        
436                         debugOutput( DEBUG_LEVEL_VERBOSE, " deleted stream (%p) from list...\n", *it);
437                         return true;
438                 }
439         }
440
441         return false; //not found
442
443 }
444
445 void IsoHandlerManager::pruneHandlers() {
446         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
447         IsoHandlerVector toUnregister;
448
449         // find all handlers that are not in use
450     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
451           it != m_IsoHandlers.end();
452           ++it )
453     {
454                 if(!((*it)->inUse())) {
455                         debugOutput( DEBUG_LEVEL_VERBOSE, " handler (%p) not in use\n",*it);
456                         toUnregister.push_back(*it);
457                 }
458     }
459         // delete them
460     for ( IsoHandlerVectorIterator it = toUnregister.begin();
461           it != toUnregister.end();
462           ++it )
463     {
464                 unregisterHandler(*it);
465                 debugOutput( DEBUG_LEVEL_VERBOSE, " deleting handler (%p)\n",*it);
466
467                 // Now the handler's been unregistered it won't be reused
468                 // again.  Therefore it really needs to be formally deleted
469                 // to free up the raw1394 handle.  Otherwise things fall
470                 // apart after several xrun recoveries as the system runs
471                 // out of resources to support all the disused but still
472                 // allocated raw1394 handles.  At least this is the current
473                 // theory as to why we end up with "memory allocation"
474                 // failures after several Xrun recoveries.
475                 delete *it;
476     }
477
478 }
479
480 bool IsoHandlerManager::startHandlers() {
481         return startHandlers(-1);
482 }
483
484 bool IsoHandlerManager::startHandlers(int cycle) {
485         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
486
487         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
488           it != m_IsoHandlers.end();
489           ++it )
490         {
491                 debugOutput( DEBUG_LEVEL_VERBOSE, " starting handler (%p)\n",*it);
492                 if(!(*it)->start(cycle)) {
493                         debugOutput( DEBUG_LEVEL_VERBOSE, " could not start handler (%p)\n",*it);
494                         return false;
495                 }
496         }
497        
498         return true;
499 }
500
501 bool IsoHandlerManager::stopHandlers() {
502         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
503
504         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
505           it != m_IsoHandlers.end();
506           ++it )
507         {
508                 debugOutput( DEBUG_LEVEL_VERBOSE, " stopping handler (%p)\n",*it);
509                 if(!(*it)->stop()){
510                         debugOutput( DEBUG_LEVEL_VERBOSE, " could not stop handler (%p)\n",*it);
511                         return false;
512                 }
513         }
514         return true;
515 }
516
517 void IsoHandlerManager::setVerboseLevel(int i) {
518         setDebugLevel(i);
519
520     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
521           it != m_IsoHandlers.end();
522           ++it )
523     {
524                 (*it)->setVerboseLevel(i);
525     }
526 }
527
528 void IsoHandlerManager::dumpInfo() {
529         debugOutputShort( DEBUG_LEVEL_NORMAL, "Dumping IsoHandlerManager Stream handler information...\n");
530         int i=0;
531
532     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
533           it != m_IsoHandlers.end();
534           ++it )
535     {
536                 debugOutputShort( DEBUG_LEVEL_NORMAL, " IsoHandler %d (%p)\n",i++,*it);
537
538                 (*it)->dumpInfo();
539     }
540
541 }
542
543 } // end of namespace FreebobStreaming
544
Note: See TracBrowser for help on using the browser.