root/trunk/libffado/src/libieee1394/IsoHandlerManager.cpp

Revision 841, 21.3 kB (checked in by ppalmers, 13 years ago)

fix single ISO thread operation (1394 stack seems to be thread-unsafe)

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 program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include "config.h"
25 #include "IsoHandlerManager.h"
26 #include "ieee1394service.h"
27 #include "IsoHandler.h"
28 #include "libstreaming/generic/StreamProcessor.h"
29
30 #include "libutil/Atomic.h"
31
32 #include "libutil/PosixThread.h"
33
34 #include <assert.h>
35
36 IMPL_DEBUG_MODULE( IsoHandlerManager, IsoHandlerManager, DEBUG_LEVEL_NORMAL );
37
38 using namespace Streaming;
39
40 IsoHandlerManager::IsoHandlerManager(Ieee1394Service& service)
41    : m_State(E_Created)
42    , m_service( service )
43    , m_realtime(false), m_priority(0)
44    , m_Thread ( NULL )
45 {}
46
47 IsoHandlerManager::IsoHandlerManager(Ieee1394Service& service, bool run_rt, int rt_prio)
48    : m_State(E_Created)
49    , m_service( service )
50    , m_realtime(run_rt), m_priority(rt_prio)
51    , m_Thread ( NULL )
52 {}
53
54 IsoHandlerManager::~IsoHandlerManager()
55 {
56     stopHandlers();
57     pruneHandlers();
58     if(m_IsoHandlers.size() > 0) {
59         debugError("Still some handlers in use\n");
60     }
61     if (m_Thread) {
62         m_Thread->Stop();
63         delete m_Thread;
64     }
65 }
66
67 bool
68 IsoHandlerManager::setThreadParameters(bool rt, int priority) {
69     debugOutput( DEBUG_LEVEL_VERBOSE, "(%p) switch to: (rt=%d, prio=%d)...\n", this, rt, priority);
70     if (priority > THREAD_MAX_RTPRIO) priority = THREAD_MAX_RTPRIO; // cap the priority
71     m_realtime = rt;
72     m_priority = priority;
73     bool result = true;
74     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
75         it != m_IsoHandlers.end();
76         ++it )
77     {
78         result &= (*it)->setThreadParameters(m_realtime, m_priority);
79     }
80
81     if (m_Thread) {
82         if (m_realtime) {
83             m_Thread->AcquireRealTime(m_priority);
84         } else {
85             m_Thread->DropRealTime();
86         }
87     }
88
89     return result;
90 }
91
92 /**
93  * Update the shadow variables. Should only be called from
94  * the iso handler iteration thread
95  */
96 void
97 IsoHandlerManager::updateShadowVars()
98 {
99     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, "updating shadow vars...\n");
100     unsigned int i, cnt, max;
101     max = m_IsoHandlers.size();
102     for (i = 0, cnt = 0; i < max; i++) {
103         IsoHandler *h = m_IsoHandlers.at(i);
104         assert(h);
105         if (h->isEnabled()) {
106             // receive handlers are always poll'ed
107             // transmit handlers only when the client is ready
108             if (h->getType() == IsoHandler::eHT_Receive || h->tryWaitForClient()) {
109                 m_IsoHandler_map_shadow[cnt] = h;
110                 m_poll_fds_shadow[cnt].fd = h->getFileDescriptor();
111                 m_poll_fds_shadow[cnt].revents = 0;
112                 m_poll_fds_shadow[cnt].events = POLLIN;
113                 cnt++;
114             } else {
115                 debugOutput(DEBUG_LEVEL_VERBOSE, "skipped handler %p\n", h);
116             }
117         }
118         if(cnt > ISOHANDLERMANAGER_MAX_ISO_HANDLERS_PER_PORT) {
119             debugWarning("Too much ISO Handlers in manager...\n");
120             break;
121         }
122     }
123     m_poll_nfds_shadow = cnt;
124     debugOutput( DEBUG_LEVEL_VERY_VERBOSE, " updated shadow vars...\n");
125 }
126
127 bool
128 IsoHandlerManager::Init() {
129     debugOutput( DEBUG_LEVEL_VERBOSE, "%p: Init thread...\n", this);
130     bool result = true;
131     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
132         it != m_IsoHandlers.end();
133         ++it )
134     {
135         result &= (*it)->Init();
136     }
137     return result;
138 }
139
140 bool
141 IsoHandlerManager::Execute() {
142     int err;
143     unsigned int i;
144
145     unsigned int m_poll_timeout = 100;
146
147     updateShadowVars();
148     // bypass if no handlers are registered
149     if (m_poll_nfds_shadow == 0) {
150         debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "bypass iterate since no handlers to poll\n");
151         usleep(m_poll_timeout * 1000);
152         return true;
153     }
154
155     // Use a shadow map of the fd's such that the poll call is not in a critical section
156     uint64_t poll_enter = m_service.getCurrentTimeAsUsecs();
157     err = poll (m_poll_fds_shadow, m_poll_nfds_shadow, m_poll_timeout);
158     uint64_t poll_exit = m_service.getCurrentTimeAsUsecs();
159
160     if (err == -1) {
161         if (errno == EINTR) {
162             return true;
163         }
164         debugFatal("poll error: %s\n", strerror (errno));
165         return false;
166     }
167
168     int nb_rcv = 0;
169     int nb_xmit = 0;
170     uint64_t iter_enter = m_service.getCurrentTimeAsUsecs();
171     for (i = 0; i < m_poll_nfds_shadow; i++) {
172         if(m_poll_fds_shadow[i].revents) {
173             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "received events: %08X for (%p)\n",
174                 m_poll_fds_shadow[i].revents, m_IsoHandler_map_shadow[i]);
175         }
176         if (m_poll_fds_shadow[i].revents & POLLERR) {
177             debugWarning("error on fd for %d\n",i);
178         }
179
180         if (m_poll_fds_shadow[i].revents & POLLHUP) {
181             debugWarning("hangup on fd for %d\n",i);
182         }
183
184         if(m_poll_fds_shadow[i].revents & (POLLIN)) {
185             if (m_IsoHandler_map_shadow[i]->getType() == IsoHandler::eHT_Receive) {
186                 m_IsoHandler_map_shadow[i]->iterate();
187                 nb_rcv++;
188             } else {
189                 // only iterate the xmit handler if it makes sense
190                 if(m_IsoHandler_map_shadow[i]->tryWaitForClient()) {
191                     m_IsoHandler_map_shadow[i]->iterate();
192                     nb_xmit++;
193                 }
194             }
195         }
196     }
197     uint64_t iter_exit = m_service.getCurrentTimeAsUsecs();
198
199     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " poll took %6lldus, iterate took %6lldus, iterated (R: %2d, X: %2d) handlers\n",
200                 poll_exit-poll_enter, iter_exit-iter_enter,
201                 nb_rcv, nb_xmit);
202
203     return true;
204 }
205
206 bool IsoHandlerManager::init()
207 {
208     debugOutput( DEBUG_LEVEL_VERBOSE, "Initializing ISO manager %p...\n", this);
209     // check state
210     if(m_State != E_Created) {
211         debugError("Manager already initialized...\n");
212         return false;
213     }
214
215 #if ISOHANDLER_PER_HANDLER_THREAD
216     // the IsoHandlers will create their own thread.
217 #else
218     // create a thread to iterate our handlers
219     debugOutput( DEBUG_LEVEL_VERBOSE, "Start thread for %p...\n", this);
220     m_Thread = new Util::PosixThread(this, m_realtime, m_priority,
221                                      PTHREAD_CANCEL_DEFERRED);
222     if(!m_Thread) {
223         debugFatal("No thread\n");
224         return false;
225     }
226     if (m_Thread->Start() != 0) {
227         debugFatal("Could not start update thread\n");
228         return false;
229     }
230 #endif
231
232     m_State=E_Running;
233     return true;
234 }
235
236 bool
237 IsoHandlerManager::disable(IsoHandler *h) {
238     bool result;
239     int i=0;
240     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Disable on IsoHandler %p\n", h);
241     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
242         it != m_IsoHandlers.end();
243         ++it )
244     {
245         if ((*it) == h) {
246             result = h->disable();
247             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " disabled\n");
248             return result;
249         }
250         i++;
251     }
252     debugError("Handler not found\n");
253     return false;
254 }
255
256 bool
257 IsoHandlerManager::enable(IsoHandler *h) {
258     bool result;
259     int i=0;
260     debugOutput(DEBUG_LEVEL_VERY_VERBOSE, "Enable on IsoHandler %p\n", h);
261     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
262         it != m_IsoHandlers.end();
263         ++it )
264     {
265         if ((*it) == h) {
266             result = h->enable();
267             debugOutput(DEBUG_LEVEL_VERY_VERBOSE, " enabled\n");
268             return result;
269         }
270         i++;
271     }
272     debugError("Handler not found\n");
273     return false;
274 }
275
276 bool IsoHandlerManager::registerHandler(IsoHandler *handler)
277 {
278     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
279     assert(handler);
280     handler->setVerboseLevel(getDebugLevel());
281     m_IsoHandlers.push_back(handler);
282     updateShadowVars();
283     return true;
284 }
285
286 bool IsoHandlerManager::unregisterHandler(IsoHandler *handler)
287 {
288     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
289     assert(handler);
290
291     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
292       it != m_IsoHandlers.end();
293       ++it )
294     {
295         if ( *it == handler ) {
296             m_IsoHandlers.erase(it);
297             updateShadowVars();
298             return true;
299         }
300     }
301     debugFatal("Could not find handler (%p)\n", handler);
302     return false; //not found
303 }
304
305 /**
306  * Registers an StreamProcessor with the IsoHandlerManager.
307  *
308  * If nescessary, an IsoHandler is created to handle this stream.
309  * Once an StreamProcessor is registered to the handler, it will be included
310  * in the ISO streaming cycle (i.e. receive/transmit of it will occur).
311  *
312  * @param stream the stream to register
313  * @return true if registration succeeds
314  *
315  * \todo : currently there is a one-to-one mapping
316  *        between streams and handlers, this is not ok for
317  *        multichannel receive
318  */
319 bool IsoHandlerManager::registerStream(StreamProcessor *stream)
320 {
321     debugOutput( DEBUG_LEVEL_VERBOSE, "Registering stream %p\n",stream);
322     assert(stream);
323
324     IsoHandler* h = NULL;
325
326     // make sure the stream isn't already attached to a handler
327     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
328       it != m_IsoHandlers.end();
329       ++it )
330     {
331         if((*it)->isStreamRegistered(stream)) {
332             debugError( "stream already registered!\n");
333             return false;
334         }
335     }
336
337     // clean up all handlers that aren't used
338     pruneHandlers();
339
340     // allocate a handler for this stream
341     if (stream->getType()==StreamProcessor::ePT_Receive) {
342         // setup the optimal parameters for the raw1394 ISO buffering
343         unsigned int packets_per_period = stream->getPacketsPerPeriod();
344         unsigned int max_packet_size = stream->getMaxPacketSize();
345         unsigned int page_size = getpagesize() - 2; // for one reason or another this is necessary
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 > page_size) {
350             debugError("max packet size (%u) > page size (%u)\n", max_packet_size, page_size);
351             return false;
352         }
353
354         unsigned int irq_interval = packets_per_period / MINIMUM_INTERRUPTS_PER_PERIOD;
355         if(irq_interval <= 0) irq_interval=1;
356        
357         // the receive buffer size doesn't matter for the latency,
358         // but it has a minimal value in order for libraw to operate correctly (300)
359         int buffers=400;
360
361         // create the actual handler
362         h = new IsoHandler(*this, IsoHandler::eHT_Receive,
363                            buffers, max_packet_size, irq_interval);
364
365         debugOutput( DEBUG_LEVEL_VERBOSE, " creating IsoRecvHandler\n");
366
367         if(!h) {
368             debugFatal("Could not create IsoRecvHandler\n");
369             return false;
370         }
371
372     } else if (stream->getType()==StreamProcessor::ePT_Transmit) {
373         // setup the optimal parameters for the raw1394 ISO buffering
374         unsigned int packets_per_period = stream->getPacketsPerPeriod();
375         unsigned int max_packet_size = stream->getMaxPacketSize();
376         unsigned int page_size = getpagesize();
377
378         // Ensure we don't request a packet size bigger than the
379         // kernel-enforced maximum which is currently 1 page.
380         if (max_packet_size > page_size) {
381             debugError("max packet size (%u) > page size (%u)\n", max_packet_size, page_size);
382             return false;
383         }
384
385         //max_packet_size = page_size; // HACK
386         unsigned int irq_interval = packets_per_period / MINIMUM_INTERRUPTS_PER_PERIOD;
387         if(irq_interval <= 0) irq_interval=1;
388
389         // the SP specifies how many packets to ISO-buffer
390         int buffers = stream->getNbPacketsIsoXmitBuffer();
391
392         debugOutput( DEBUG_LEVEL_VERBOSE, " creating IsoXmitHandler\n");
393
394         // create the actual handler
395         h = new IsoHandler(*this, IsoHandler::eHT_Transmit,
396                            buffers, max_packet_size, irq_interval);
397
398         if(!h) {
399             debugFatal("Could not create IsoXmitHandler\n");
400             return false;
401         }
402
403     } else {
404         debugFatal("Bad stream type\n");
405         return false;
406     }
407
408     h->setVerboseLevel(getDebugLevel());
409
410     // init the handler
411     if(!h->init()) {
412         debugFatal("Could not initialize receive handler\n");
413         return false;
414     }
415
416     // set the handler's thread parameters
417     // receive handlers have lower priority than the client thread
418     // since they have ISO side buffering
419     // xmit handlers have higher priority since we want client side
420     // frames to be put into the ISO buffers ASAP
421     int thread_prio;
422     if (stream->getType()==StreamProcessor::ePT_Receive) {
423         thread_prio = m_priority - 1;
424         if (thread_prio < THREAD_MIN_RTPRIO) thread_prio = THREAD_MIN_RTPRIO;
425     } else {
426         thread_prio = m_priority + 1;
427         if (thread_prio > THREAD_MAX_RTPRIO) thread_prio = THREAD_MAX_RTPRIO;
428     }
429
430     if(!h->setThreadParameters(m_realtime, thread_prio)) {
431         debugFatal("Could not set handler thread parameters\n");
432         return false;
433     }
434
435     // register the stream with the handler
436     if(!h->registerStream(stream)) {
437         debugFatal("Could not register receive stream with handler\n");
438         return false;
439     }
440
441     // register the handler with the manager
442     if(!registerHandler(h)) {
443         debugFatal("Could not register receive handler with manager\n");
444         return false;
445     }
446     debugOutput( DEBUG_LEVEL_VERBOSE, " registered stream (%p) with handler (%p)\n", stream, h);
447
448     m_StreamProcessors.push_back(stream);
449     debugOutput( DEBUG_LEVEL_VERBOSE, " %d streams, %d handlers registered\n",
450                                       m_StreamProcessors.size(), m_IsoHandlers.size());
451     return true;
452 }
453
454 bool IsoHandlerManager::unregisterStream(StreamProcessor *stream)
455 {
456     debugOutput( DEBUG_LEVEL_VERBOSE, "Unregistering stream %p\n",stream);
457     assert(stream);
458
459     // make sure the stream isn't attached to a handler anymore
460     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
461       it != m_IsoHandlers.end();
462       ++it )
463     {
464         if((*it)->isStreamRegistered(stream)) {
465             if(!(*it)->unregisterStream(stream)) {
466                 debugOutput( DEBUG_LEVEL_VERBOSE, " could not unregister stream (%p) from handler (%p)...\n",stream,*it);
467                 return false;
468             }
469             debugOutput( DEBUG_LEVEL_VERBOSE, " unregistered stream (%p) from handler (%p)...\n",stream,*it);
470         }
471     }
472
473     // clean up all handlers that aren't used
474     pruneHandlers();
475
476     // remove the stream from the registered streams list
477     for ( StreamProcessorVectorIterator it = m_StreamProcessors.begin();
478       it != m_StreamProcessors.end();
479       ++it )
480     {
481         if ( *it == stream ) {
482             m_StreamProcessors.erase(it);
483             debugOutput( DEBUG_LEVEL_VERBOSE, " deleted stream (%p) from list...\n", *it);
484             return true;
485         }
486     }
487     return false; //not found
488 }
489
490 /**
491  * @brief unregister a handler from the manager
492  * @note called without the lock held.
493  */
494 void IsoHandlerManager::pruneHandlers() {
495     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
496     IsoHandlerVector toUnregister;
497
498     // find all handlers that are not in use
499     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
500           it != m_IsoHandlers.end();
501           ++it )
502     {
503         if(!((*it)->inUse())) {
504             debugOutput( DEBUG_LEVEL_VERBOSE, " handler (%p) not in use\n",*it);
505             toUnregister.push_back(*it);
506         }
507     }
508     // delete them
509     for ( IsoHandlerVectorIterator it = toUnregister.begin();
510           it != toUnregister.end();
511           ++it )
512     {
513         unregisterHandler(*it);
514
515         debugOutput( DEBUG_LEVEL_VERBOSE, " deleting handler (%p)\n",*it);
516
517         // Now the handler's been unregistered it won't be reused
518         // again.  Therefore it really needs to be formally deleted
519         // to free up the raw1394 handle.  Otherwise things fall
520         // apart after several xrun recoveries as the system runs
521         // out of resources to support all the disused but still
522         // allocated raw1394 handles.  At least this is the current
523         // theory as to why we end up with "memory allocation"
524         // failures after several Xrun recoveries.
525         delete *it;
526     }
527 }
528
529 bool
530 IsoHandlerManager::stopHandlerForStream(Streaming::StreamProcessor *stream) {
531     // check state
532     if(m_State != E_Running) {
533         debugError("Incorrect state, expected E_Running, got %s\n", eHSToString(m_State));
534         return false;
535     }
536     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
537       it != m_IsoHandlers.end();
538       ++it )
539     {
540         if((*it)->isStreamRegistered(stream)) {
541             bool result;
542             debugOutput( DEBUG_LEVEL_VERBOSE, " stopping handler %p for stream %p\n", *it, stream);
543             result = (*it)->disable();
544             if(!result) {
545                 debugOutput( DEBUG_LEVEL_VERBOSE, " could not disable handler (%p)\n",*it);
546                 return false;
547             }
548             return true;
549         }
550     }
551     debugError("Stream %p has no attached handler\n", stream);
552     return false;
553 }
554
555 int
556 IsoHandlerManager::getPacketLatencyForStream(Streaming::StreamProcessor *stream) {
557     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
558       it != m_IsoHandlers.end();
559       ++it )
560     {
561         if((*it)->isStreamRegistered(stream)) {
562             return (*it)->getPacketLatency();
563         }
564     }
565     debugError("Stream %p has no attached handler\n", stream);
566     return 0;
567 }
568
569 void
570 IsoHandlerManager::flushHandlerForStream(Streaming::StreamProcessor *stream) {
571     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
572       it != m_IsoHandlers.end();
573       ++it )
574     {
575         if((*it)->isStreamRegistered(stream)) {
576             return (*it)->flush();
577         }
578     }
579     debugError("Stream %p has no attached handler\n", stream);
580     return;
581 }
582
583 bool
584 IsoHandlerManager::startHandlerForStream(Streaming::StreamProcessor *stream) {
585     return startHandlerForStream(stream, -1);
586 }
587
588 bool
589 IsoHandlerManager::startHandlerForStream(Streaming::StreamProcessor *stream, int cycle) {
590     // check state
591     if(m_State != E_Running) {
592         debugError("Incorrect state, expected E_Running, got %s\n", eHSToString(m_State));
593         return false;
594     }
595     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
596       it != m_IsoHandlers.end();
597       ++it )
598     {
599         if((*it)->isStreamRegistered(stream)) {
600             bool result;
601             debugOutput( DEBUG_LEVEL_VERBOSE, " starting handler %p for stream %p\n", *it, stream);
602             result = (*it)->enable(cycle);
603             if(!result) {
604                 debugOutput( DEBUG_LEVEL_VERBOSE, " could not enable handler (%p)\n",*it);
605                 return false;
606             }
607             return true;
608         }
609     }
610     debugError("Stream %p has no attached handler\n", stream);
611     return false;
612 }
613
614 bool IsoHandlerManager::stopHandlers() {
615     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
616
617     // check state
618     if(m_State != E_Running) {
619         debugError("Incorrect state, expected E_Running, got %s\n", eHSToString(m_State));
620         return false;
621     }
622
623     bool retval=true;
624
625     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
626         it != m_IsoHandlers.end();
627         ++it )
628     {
629         debugOutput( DEBUG_LEVEL_VERBOSE, "Stopping handler (%p)\n",*it);
630         if(!(*it)->disable()){
631             debugOutput( DEBUG_LEVEL_VERBOSE, " could not stop handler (%p)\n",*it);
632             retval=false;
633         }
634     }
635
636     if (retval) {
637         m_State=E_Prepared;
638     } else {
639         m_State=E_Error;
640     }
641     return retval;
642 }
643
644 bool IsoHandlerManager::reset() {
645     debugOutput( DEBUG_LEVEL_VERBOSE, "enter...\n");
646     // check state
647     if(m_State == E_Error) {
648         debugFatal("Resetting from error condition not yet supported...\n");
649         return false;
650     }
651     // if not in an error condition, reset means stop the handlers
652     return stopHandlers();
653 }
654
655 void IsoHandlerManager::setVerboseLevel(int i) {
656     setDebugLevel(i);
657     // propagate the debug level
658     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
659           it != m_IsoHandlers.end();
660           ++it )
661     {
662         (*it)->setVerboseLevel(i);
663     }
664 }
665
666 void IsoHandlerManager::dumpInfo() {
667     int i=0;
668     debugOutputShort( DEBUG_LEVEL_NORMAL, "Dumping IsoHandlerManager Stream handler information...\n");
669     debugOutputShort( DEBUG_LEVEL_NORMAL, " State: %d\n",(int)m_State);
670
671     for ( IsoHandlerVectorIterator it = m_IsoHandlers.begin();
672           it != m_IsoHandlers.end();
673           ++it )
674     {
675         debugOutputShort( DEBUG_LEVEL_NORMAL, " IsoHandler %d (%p)\n",i++,*it);
676         (*it)->dumpInfo();
677     }
678 }
679
680 const char *
681 IsoHandlerManager::eHSToString(enum eHandlerStates s) {
682     switch (s) {
683         default: return "Invalid";
684         case E_Created: return "Created";
685         case E_Prepared: return "Prepared";
686         case E_Running: return "Running";
687         case E_Error: return "Error";
688     }
689 }
Note: See TracBrowser for help on using the browser.