root/branches/libfreebob-2.0/src/libstreaming/IsoHandlerManager.cpp

Revision 269, 14.2 kB (checked in by jwoithe, 16 years ago)

More MOTU port infrastructure developments.
Make teststreaming2 compile and maybe work.
Fix some comments in IsoHandlerManager?.cpp and freebob_streaming.cpp.

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 // Intel recommends that a serializing instruction
60 // should be called before and after rdtsc.
61 // CPUID is a serializing instruction.
62 #define read_rdtsc(time) \
63         __asm__ __volatile__( \
64         "pushl %%ebx\n\t" \
65         "cpuid\n\t" \
66         "rdtsc\n\t" \
67         "mov %%eax,(%0)\n\t" \
68         "cpuid\n\t" \
69         "popl %%ebx\n\t" \
70         : /* no output */ \
71         : "S"(&time) \
72         : "eax", "ecx", "edx", "memory")
73
74 static inline unsigned long debugGetCurrentUTime() {
75     unsigned retval;
76     read_rdtsc(retval);
77     return retval;
78 }
79
80 bool IsoHandlerManager::Execute()
81 {
82         int err;
83         int i=0;
84         debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "enter...\n");
85        
86         unsigned long tstamp=debugGetCurrentUTime();
87
88         err = poll (m_poll_fds, m_poll_nfds, m_poll_timeout);
89        
90 //      debugOutput(DEBUG_LEVEL_VERBOSE, "Poll took: %6d\n", debugGetCurrentUTime()-tstamp);
91        
92         if (err == -1) {
93                 if (errno == EINTR) {
94                         return true;
95                 }
96                 debugFatal("poll error: %s\n", strerror (errno));
97                 return false;
98         }
99
100         for (i = 0; i < m_poll_nfds; i++) {
101                 if (m_poll_fds[i].revents & POLLERR) {
102                         debugWarning("error on fd for %d\n",i);
103                 }
104
105                 if (m_poll_fds[i].revents & POLLHUP) {
106                         debugWarning("hangup on fd for %d\n",i);
107                 }
108                
109                 if(m_poll_fds[i].revents & (POLLIN)) {
110                         IsoHandler *s=m_IsoHandlers.at(i);
111                         assert(s);
112                        
113                         unsigned int packetcount_prev=s->getPacketCount();
114                        
115                         tstamp=debugGetCurrentUTime();
116                        
117                         s->iterate();
118 /*                      debugOutput(DEBUG_LEVEL_VERBOSE, "Iterate %p: time: %6d | packets: %3d\n",
119                              s, debugGetCurrentUTime()-tstamp, s->getPacketCount()-packetcount_prev
120                              );*/
121                 }
122         }
123         return true;
124
125 }
126
127 bool IsoHandlerManager::prepare()
128 {
129         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
130     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
131           it != m_IsoHandlers.end();
132           ++it )
133     {
134         if(!(*it)->prepare()) {
135                         debugFatal("Could not prepare handlers\n");
136                         return false;
137         }
138     }
139
140         return true;
141 }
142
143
144
145 bool IsoHandlerManager::registerHandler(IsoHandler *handler)
146 {
147         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
148         assert(handler);
149        
150         m_IsoHandlers.push_back(handler);
151        
152         handler->setVerboseLevel(getDebugLevel());
153
154         // rebuild the fd map for poll()'ing.
155         return rebuildFdMap(); 
156
157 }
158
159 bool IsoHandlerManager::unregisterHandler(IsoHandler *handler)
160 {
161         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
162         assert(handler);
163
164         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
165           it != m_IsoHandlers.end();
166           ++it )
167         {
168                 if ( *it == handler ) {
169                         // erase the iso handler from the list
170                         m_IsoHandlers.erase(it);
171                         // rebuild the fd map for poll()'ing.
172                         return rebuildFdMap();
173                 }
174         }
175
176         debugFatal("Could not find handler (%p)\n", handler);
177        
178         return false; //not found
179
180 }
181
182 bool IsoHandlerManager::rebuildFdMap() {
183         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
184         int i=0;
185
186         m_poll_nfds=0;
187         if(m_poll_fds) free(m_poll_fds);
188
189         // count the number of handlers
190         m_poll_nfds=m_IsoHandlers.size();
191
192         // allocate the fd array
193         m_poll_fds   = (struct pollfd *) calloc (m_poll_nfds, sizeof (struct pollfd));
194         if(!m_poll_fds) {
195                 debugFatal("Could not allocate memory for poll FD array\n");
196                 return false;
197         }
198
199         // fill the fd map
200         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
201           it != m_IsoHandlers.end();
202           ++it )
203         {
204                 m_poll_fds[i].fd=(*it)->getFileDescriptor();
205                 m_poll_fds[i].events = POLLIN;
206                 i++;
207         }
208
209         return true;
210 }
211
212 void IsoHandlerManager::disablePolling(IsoStream *stream) {
213     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Disable polling on stream %p\n",stream);
214         int i=0;
215         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
216           it != m_IsoHandlers.end();
217           ++it )
218         {
219            if ((*it)->isStreamRegistered(stream)) {
220                m_poll_fds[i].events = 0;
221                m_poll_fds[i].revents = 0;
222             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "polling disabled\n");
223            }
224            i++;
225         }
226
227 }
228
229 void IsoHandlerManager::enablePolling(IsoStream *stream) {
230     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Enable polling on stream %p\n",stream);
231         int i=0;
232         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
233           it != m_IsoHandlers.end();
234           ++it )
235         {
236            if ((*it)->isStreamRegistered(stream)) {
237                m_poll_fds[i].events = POLLIN;
238                m_poll_fds[i].revents = 0;
239             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "polling enabled\n");
240            }
241            i++;
242         }
243 }
244
245
246 /**
247  * Registers an IsoStream with the IsoHandlerManager.
248  *
249  * If nescessary, an IsoHandler is created to handle this stream.
250  * Once an IsoStream is registered to the handler, it will be included
251  * in the ISO streaming cycle (i.e. receive/transmit of it will occur).
252  *
253  * @param stream the stream to register
254  * @return true if registration succeeds
255  *
256  * \todo : currently there is a one-to-one mapping
257  *        between streams and handlers, this is not ok for
258  *        multichannel receive
259  */
260 bool IsoHandlerManager::registerStream(IsoStream *stream)
261 {
262         debugOutput( DEBUG_LEVEL_VERBOSE, "Registering stream %p\n",stream);
263         assert(stream);
264
265         // make sure the stream isn't already attached to a handler
266         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
267           it != m_IsoHandlers.end();
268           ++it )
269         {
270                 if((*it)->isStreamRegistered(stream)) {
271                         debugWarning( "stream already registered!\n");
272                         (*it)->unregisterStream(stream);
273                        
274                 }
275         }
276        
277         // clean up all handlers that aren't used
278         pruneHandlers();
279
280         // allocate a handler for this stream
281         if (stream->getType()==IsoStream::EST_Receive) {
282                 // setup the optimal parameters for the raw1394 ISO buffering
283                 unsigned int packets_per_period=stream->getPacketsPerPeriod();
284                
285                 // hardware interrupts occur when one DMA block is full, and the size of one DMA
286                 // block = PAGE_SIZE. Setting the max_packet_size makes sure that the HW irq is
287                 // occurs at a period boundary (optimal CPU use)
288                
289                 // NOTE: try and use 4 hardware interrupts per period for better latency.
290                 unsigned int max_packet_size=4 * getpagesize() / packets_per_period;
291                 if (max_packet_size < stream->getMaxPacketSize()) {
292                         max_packet_size=stream->getMaxPacketSize();
293                 }
294
295                 // Ensure we don't request a packet size bigger than the
296                 // kernel-enforced maximum which is currently 1 page.
297                 if (max_packet_size > (unsigned int)getpagesize())
298                         max_packet_size = getpagesize();
299
300                 int irq_interval=packets_per_period / 4;
301         if(irq_interval <= 0) irq_interval=1;
302
303                 /* the receive buffer size doesn't matter for the latency,
304                    but it has a minimal value in order for libraw to operate correctly (300) */
305                 int buffers=400;
306                
307                 // create the actual handler
308                 IsoRecvHandler *h = new IsoRecvHandler(stream->getPort(), buffers,
309                                                        max_packet_size, irq_interval);
310
311                 debugOutput( DEBUG_LEVEL_VERBOSE, " registering IsoRecvHandler\n");
312
313                 if(!h) {
314                         debugFatal("Could not create IsoRecvHandler\n");
315                         return false;
316                 }
317
318                 h->setVerboseLevel(getDebugLevel());
319
320                 // init the handler
321                 if(!h->init()) {
322                         debugFatal("Could not initialize receive handler\n");
323                         return false;
324                 }
325
326                 // register the stream with the handler
327                 if(!h->registerStream(stream)) {
328                         debugFatal("Could not register receive stream with handler\n");
329                         return false;
330                 }
331
332                 // register the handler with the manager
333                 if(!registerHandler(h)) {
334                         debugFatal("Could not register receive handler with manager\n");
335                         return false;
336                 }
337                 debugOutput( DEBUG_LEVEL_VERBOSE, " registered stream (%p) with handler (%p)\n",stream,h);
338         }
339        
340         if (stream->getType()==IsoStream::EST_Transmit) {
341        
342                 // setup the optimal parameters for the raw1394 ISO buffering
343                 unsigned int packets_per_period=stream->getPacketsPerPeriod();
344                 // hardware interrupts occur when one DMA block is full, and the size of one DMA
345                 // block = PAGE_SIZE. Setting the max_packet_size makes sure that the HW irq 
346                 // occurs at a period boundary (optimal CPU use)
347                 // NOTE: try and use 4 interrupts per period for better latency.
348                 unsigned int max_packet_size=4 * getpagesize() / packets_per_period;
349                 if (max_packet_size < stream->getMaxPacketSize()) {
350                         max_packet_size=stream->getMaxPacketSize();
351                 }
352
353                 // Ensure we don't request a packet size bigger than the
354                 // kernel-enforced maximum which is currently 1 page.
355                 if (max_packet_size > (unsigned int)getpagesize())
356                         max_packet_size = getpagesize();
357
358                 int irq_interval=packets_per_period / 4;
359                 if(irq_interval <= 0) irq_interval=1;
360        
361                 // the transmit buffer size should be as low as possible for latency.
362                 // note however that the raw1394 subsystem tries to keep this buffer
363                 // full, so we have to make sure that we have enough events in our
364                 // event buffers
365                 int buffers=packets_per_period;
366                
367                 // NOTE: this is dangerous: what if there is not enough prefill?
368 //              if (buffers<10) buffers=10;     
369                
370                 // create the actual handler
371                 IsoXmitHandler *h = new IsoXmitHandler(stream->getPort(), buffers,
372                                                        max_packet_size, irq_interval);
373
374                 debugOutput( DEBUG_LEVEL_VERBOSE, " registering IsoXmitHandler\n");
375
376                 if(!h) {
377                         debugFatal("Could not create IsoXmitHandler\n");
378                         return false;
379                 }
380
381                 h->setVerboseLevel(getDebugLevel());
382
383                 // init the handler
384                 if(!h->init()) {
385                         debugFatal("Could not initialize transmit handler\n");
386                         return false;
387                 }
388
389                 // register the stream with the handler
390                 if(!h->registerStream(stream)) {
391                         debugFatal("Could not register transmit stream with handler\n");
392                         return false;
393                 }
394
395                 // register the handler with the manager
396                 if(!registerHandler(h)) {
397                         debugFatal("Could not register transmit handler with manager\n");
398                         return false;
399                 }
400                 debugOutput( DEBUG_LEVEL_VERBOSE, " registered stream (%p) with handler (%p)\n",stream,h);
401
402         }
403
404         m_IsoStreams.push_back(stream);
405         debugOutput( DEBUG_LEVEL_VERBOSE, " %d streams, %d handlers registered\n",
406                                           m_IsoStreams.size(), m_IsoHandlers.size());
407
408         return true;
409 }
410
411 bool IsoHandlerManager::unregisterStream(IsoStream *stream)
412 {
413         debugOutput( DEBUG_LEVEL_VERBOSE, "Unregistering stream %p\n",stream);
414         assert(stream);
415
416         // make sure the stream isn't attached to a handler anymore
417         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
418           it != m_IsoHandlers.end();
419           ++it )
420         {
421                 if((*it)->isStreamRegistered(stream)) {
422                         if(!(*it)->unregisterStream(stream)) {
423                                 debugOutput( DEBUG_LEVEL_VERBOSE, " could not unregister stream (%p) from handler (%p)...\n",stream,*it);
424                                 return false;
425                         }
426                        
427                         debugOutput( DEBUG_LEVEL_VERBOSE, " unregistered stream (%p) from handler (%p)...\n",stream,*it);
428                 }
429         }
430
431         // clean up all handlers that aren't used
432         pruneHandlers();
433
434         // remove the stream from the registered streams list
435         for ( IsoStreamVectorIterator it = m_IsoStreams.begin();
436           it != m_IsoStreams.end();
437           ++it )
438         {
439                 if ( *it == stream ) {
440                         m_IsoStreams.erase(it);
441                        
442                         debugOutput( DEBUG_LEVEL_VERBOSE, " deleted stream (%p) from list...\n", *it);
443                         return true;
444                 }
445         }
446
447         return false; //not found
448
449 }
450
451 void IsoHandlerManager::pruneHandlers() {
452         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
453         IsoHandlerVector toUnregister;
454
455         // find all handlers that are not in use
456     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
457           it != m_IsoHandlers.end();
458           ++it )
459     {
460                 if(!((*it)->inUse())) {
461                         debugOutput( DEBUG_LEVEL_VERBOSE, " handler (%p) not in use\n",*it);
462                         toUnregister.push_back(*it);
463                 }
464     }
465         // delete them
466     for ( IsoHandlerVectorIterator it = toUnregister.begin();
467           it != toUnregister.end();
468           ++it )
469     {
470                 unregisterHandler(*it);
471                 debugOutput( DEBUG_LEVEL_VERBOSE, " deleting handler (%p)\n",*it);
472     }
473
474 }
475
476 bool IsoHandlerManager::startHandlers() {
477         return startHandlers(-1);
478 }
479
480 bool IsoHandlerManager::startHandlers(int cycle) {
481         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
482
483         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
484           it != m_IsoHandlers.end();
485           ++it )
486         {
487                 debugOutput( DEBUG_LEVEL_VERBOSE, " starting handler (%p)\n",*it);
488                 if(!(*it)->start(cycle)) {
489                         debugOutput( DEBUG_LEVEL_VERBOSE, " could not start handler (%p)\n",*it);
490                         return false;
491                 }
492         }
493        
494         return true;
495 }
496
497 bool IsoHandlerManager::stopHandlers() {
498         debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
499
500         for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
501           it != m_IsoHandlers.end();
502           ++it )
503         {
504                 debugOutput( DEBUG_LEVEL_VERBOSE, " stopping handler (%p)\n",*it);
505                 if(!(*it)->stop()){
506                         debugOutput( DEBUG_LEVEL_VERBOSE, " could not stop handler (%p)\n",*it);
507                         return false;
508                 }
509         }
510         return true;
511 }
512
513 void IsoHandlerManager::setVerboseLevel(int i) {
514         setDebugLevel(i);
515
516     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
517           it != m_IsoHandlers.end();
518           ++it )
519     {
520                 (*it)->setVerboseLevel(i);
521     }
522 }
523
524 void IsoHandlerManager::dumpInfo() {
525         debugOutputShort( DEBUG_LEVEL_NORMAL, "Dumping IsoHandlerManager Stream handler information...\n");
526         int i=0;
527
528     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
529           it != m_IsoHandlers.end();
530           ++it )
531     {
532                 debugOutputShort( DEBUG_LEVEL_NORMAL, " Stream %d (%p)\n",i++,*it);
533
534                 (*it)->dumpInfo();
535     }
536
537 }
538
539 } // end of namespace FreebobStreaming
540
Note: See TracBrowser for help on using the browser.