root/branches/libffado-2.0/src/bebob/bebob_avdevice.cpp

Revision 1213, 20.1 kB (checked in by ppalmers, 13 years ago)

merge trunk changes r1209:1212 (svn merge -r 1209:1212 svn+ssh://ffadosvn@ffado.org/ffado/trunk/libffado)

Line 
1 /*
2  * Copyright (C) 2005-2008 by Daniel Wagner
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 2 of the License, or
12  * (at your option) version 3 of the License.
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
26 #include "bebob/bebob_avdevice.h"
27 #include "bebob/bebob_avdevice_subunit.h"
28 #include "bebob/bebob_mixer.h"
29
30 #include "bebob/focusrite/focusrite_saffire.h"
31 #include "bebob/focusrite/focusrite_saffirepro.h"
32 #include "bebob/terratec/terratec_device.h"
33 #include "bebob/mackie/onyxmixer.h"
34 #include "bebob/edirol/edirol_fa101.h"
35 #include "bebob/edirol/edirol_fa66.h"
36 #include "bebob/esi/quatafire610.h"
37
38 #include "libieee1394/configrom.h"
39 #include "libieee1394/ieee1394service.h"
40
41 #include "genericavc/avc_vendormodel.h"
42
43 #include "libavc/general/avc_plug_info.h"
44 #include "libavc/general/avc_extended_plug_info.h"
45 #include "libavc/general/avc_subunit_info.h"
46 #include "libavc/streamformat/avc_extended_stream_format.h"
47 #include "libutil/cmd_serialize.h"
48 #include "libavc/avc_definitions.h"
49
50 #include "debugmodule/debugmodule.h"
51
52 #include <iostream>
53 #include <sstream>
54 #include <unistd.h>
55 #include <sys/stat.h>
56
57 using namespace AVC;
58
59 namespace BeBoB {
60
61 AvDevice::AvDevice( DeviceManager& d, std::auto_ptr< ConfigRom >( configRom ) )
62     : GenericAVC::AvDevice( d, configRom )
63     , m_Mixer ( 0 )
64 {
65     debugOutput( DEBUG_LEVEL_VERBOSE, "Created BeBoB::AvDevice (NodeID %d)\n",
66                  getConfigRom().getNodeId() );
67
68     // DM1500 based devices seem to upset the linux1394 stack when commands are
69     // sent too fast.
70     if (AVC::AVCCommand::getSleepAfterAVCCommand() < 200) {
71         AVC::AVCCommand::setSleepAfterAVCCommand( 200 );
72     }
73
74 }
75
76 AvDevice::~AvDevice()
77 {
78     destroyMixer();
79 }
80
81 bool
82 AvDevice::probe( ConfigRom& configRom )
83 {
84     unsigned int vendorId = configRom.getNodeVendorId();
85     unsigned int modelId = configRom.getModelId();
86
87     GenericAVC::VendorModel vendorModel( SHAREDIR "/ffado_driver_bebob.txt" );
88     if ( vendorModel.parse() ) {
89         return vendorModel.isPresent( vendorId, modelId );
90     }
91
92     return false;
93 }
94
95 FFADODevice *
96 AvDevice::createDevice(DeviceManager& d, std::auto_ptr<ConfigRom>( configRom ))
97 {
98     unsigned int vendorId = configRom->getNodeVendorId();
99     unsigned int modelId = configRom->getModelId();
100
101     switch (vendorId) {
102         case FW_VENDORID_MACKIE:
103             if (modelId == 0x00010065 ) {
104                 return new Mackie::OnyxMixerDevice(d, configRom);
105             }
106         case FW_VENDORID_EDIROL:
107             switch (modelId) {
108                 case 0x00010048:
109                     return new Edirol::EdirolFa101Device(d, configRom);
110                 case 0x00010049:
111                     return new Edirol::EdirolFa66Device(d, configRom);
112                 default:
113                     return new AvDevice(d, configRom);
114             }
115         case FW_VENDORID_ESI:
116             if (modelId == 0x00010064) {
117                 return new ESI::QuataFireDevice(d, configRom);
118             }
119             break;
120         case FW_VENDORID_TERRATEC:
121             switch(modelId) {
122                 case 0x00000003:
123                     return new Terratec::Phase88Device(d, configRom);
124                 default: // return a plain BeBoB device
125                     return new AvDevice(d, configRom);
126             }
127         case FW_VENDORID_FOCUSRITE:
128             switch(modelId) {
129                 case 0x00000003:
130                 case 0x00000006:
131                     return new Focusrite::SaffireProDevice(d, configRom);
132                 case 0x00000000:
133                     return new Focusrite::SaffireDevice(d, configRom);
134                 default: // return a plain BeBoB device
135                     return new AvDevice(d, configRom);
136            }
137         default:
138             return new AvDevice(d, configRom);
139     }
140     return NULL;
141 }
142
143 bool
144 AvDevice::discover()
145 {
146     unsigned int vendorId = getConfigRom().getNodeVendorId();
147     unsigned int modelId = getConfigRom().getModelId();
148
149     GenericAVC::VendorModel vendorModel( SHAREDIR "/ffado_driver_bebob.txt" );
150     if ( vendorModel.parse() ) {
151         m_model = vendorModel.find( vendorId, modelId );
152     }
153
154     if (GenericAVC::VendorModel::isValid(m_model)) {
155         debugOutput( DEBUG_LEVEL_VERBOSE, "found %s %s\n",
156                      m_model.vendor_name.c_str(),
157                      m_model.model_name.c_str());
158     } else return false;
159
160     if ( !Unit::discover() ) {
161         debugError( "Could not discover unit\n" );
162         return false;
163     }
164
165     if((getAudioSubunit( 0 ) == NULL)) {
166         debugError( "Unit doesn't have an Audio subunit.\n");
167         return false;
168     }
169     if((getMusicSubunit( 0 ) == NULL)) {
170         debugError( "Unit doesn't have a Music subunit.\n");
171         return false;
172     }
173
174     if(!buildMixer()) {
175         debugWarning("Could not build mixer\n");
176     }
177     return true;
178 }
179
180 bool
181 AvDevice::buildMixer()
182 {
183     debugOutput(DEBUG_LEVEL_VERBOSE, "Building a generic BeBoB mixer...\n");
184     // create a Mixer
185     // this removes the mixer if it already exists
186     // note: a mixer self-registers to it's parent
187     delete m_Mixer;
188
189     // create the mixer & register it
190     if(getAudioSubunit(0) == NULL) {
191         debugWarning("Could not find audio subunit, mixer not available.\n");
192         m_Mixer = NULL;
193     } else {
194         m_Mixer = new Mixer(*this);
195     }
196     if (m_Mixer) m_Mixer->setVerboseLevel(getDebugLevel());
197     return m_Mixer != NULL;
198 }
199
200 bool
201 AvDevice::destroyMixer()
202 {
203     delete m_Mixer;
204     return true;
205 }
206
207 bool
208 AvDevice::setSelectorFBValue(int id, int value) {
209     FunctionBlockCmd fbCmd( get1394Service(),
210                             FunctionBlockCmd::eFBT_Selector,
211                             id,
212                             FunctionBlockCmd::eCA_Current );
213     fbCmd.setNodeId( getNodeId() );
214     fbCmd.setSubunitId( 0x00 );
215     fbCmd.setCommandType( AVCCommand::eCT_Control );
216     fbCmd.m_pFBSelector->m_inputFbPlugNumber = (value & 0xFF);
217     fbCmd.setVerboseLevel( getDebugLevel() );
218
219     if ( !fbCmd.fire() ) {
220         debugError( "cmd failed\n" );
221         return false;
222     }
223
224 //     if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) {
225 //         Util::CoutSerializer se;
226 //         fbCmd.serialize( se );
227 //     }
228 //     
229     if((fbCmd.getResponse() != AVCCommand::eR_Accepted)) {
230         debugWarning("fbCmd.getResponse() != AVCCommand::eR_Accepted\n");
231     }
232
233     return (fbCmd.getResponse() == AVCCommand::eR_Accepted);
234 }
235
236 int
237 AvDevice::getSelectorFBValue(int id) {
238
239     FunctionBlockCmd fbCmd( get1394Service(),
240                             FunctionBlockCmd::eFBT_Selector,
241                             id,
242                             FunctionBlockCmd::eCA_Current );
243     fbCmd.setNodeId( getNodeId() );
244     fbCmd.setSubunitId( 0x00 );
245     fbCmd.setCommandType( AVCCommand::eCT_Status );
246     fbCmd.m_pFBSelector->m_inputFbPlugNumber = 0xFF;
247     fbCmd.setVerboseLevel( getDebugLevel() );
248
249     if ( !fbCmd.fire() ) {
250         debugError( "cmd failed\n" );
251         return -1;
252     }
253    
254 //     if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) {
255 //         Util::CoutSerializer se;
256 //         fbCmd.serialize( se );
257 //     }
258
259     if((fbCmd.getResponse() != AVCCommand::eR_Implemented)) {
260         debugWarning("fbCmd.getResponse() != AVCCommand::eR_Implemented\n");
261     }
262    
263     return fbCmd.m_pFBSelector->m_inputFbPlugNumber;
264 }
265
266 bool
267 AvDevice::setFeatureFBVolumeValue(int id, int channel, int v) {
268
269     FunctionBlockCmd fbCmd( get1394Service(),
270                             FunctionBlockCmd::eFBT_Feature,
271                             id,
272                             FunctionBlockCmd::eCA_Current );
273     fbCmd.setNodeId( getNodeId() );
274     fbCmd.setSubunitId( 0x00 );
275     fbCmd.setCommandType( AVCCommand::eCT_Control );
276     fbCmd.m_pFBFeature->m_audioChannelNumber = channel;
277     fbCmd.m_pFBFeature->m_controlSelector = FunctionBlockFeature::eCSE_Feature_Volume;
278     fbCmd.m_pFBFeature->m_pVolume->m_volume = v;
279     fbCmd.setVerboseLevel( getDebugLevel() );
280
281     if ( !fbCmd.fire() ) {
282         debugError( "cmd failed\n" );
283         return false;
284     }
285
286 //     if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) {
287 //         Util::CoutSerializer se;
288 //         fbCmd.serialize( se );
289 //     }
290    
291     if((fbCmd.getResponse() != AVCCommand::eR_Accepted)) {
292         debugWarning("fbCmd.getResponse() != AVCCommand::eR_Accepted\n");
293     }
294
295     return (fbCmd.getResponse() == AVCCommand::eR_Accepted);
296 }
297
298 int
299 AvDevice::getFeatureFBVolumeValue(int id, int channel) {
300     FunctionBlockCmd fbCmd( get1394Service(),
301                             FunctionBlockCmd::eFBT_Feature,
302                             id,
303                             FunctionBlockCmd::eCA_Current );
304     fbCmd.setNodeId( getNodeId() );
305     fbCmd.setSubunitId( 0x00 );
306     fbCmd.setCommandType( AVCCommand::eCT_Status );
307     fbCmd.m_pFBFeature->m_audioChannelNumber = channel;
308     fbCmd.m_pFBFeature->m_controlSelector = FunctionBlockFeature::eCSE_Feature_Volume;
309     fbCmd.m_pFBFeature->m_pVolume->m_volume = 0;
310     fbCmd.setVerboseLevel( getDebugLevel() );
311
312     if ( !fbCmd.fire() ) {
313         debugError( "cmd failed\n" );
314         return 0;
315     }
316    
317 //     if ( getDebugLevel() >= DEBUG_LEVEL_NORMAL ) {
318 //         Util::CoutSerializer se;
319 //         fbCmd.serialize( se );
320 //     }
321
322     if((fbCmd.getResponse() != AVCCommand::eR_Implemented)) {
323         debugWarning("fbCmd.getResponse() != AVCCommand::eR_Implemented\n");
324     }
325    
326     int16_t volume=(int16_t)(fbCmd.m_pFBFeature->m_pVolume->m_volume);
327    
328     return volume;
329 }
330
331 void
332 AvDevice::showDevice()
333 {
334     debugOutput(DEBUG_LEVEL_NORMAL, "Device is a BeBoB device\n");
335     GenericAVC::AvDevice::showDevice();
336     flushDebugOutput();
337 }
338
339 void
340 AvDevice::setVerboseLevel(int l)
341 {
342     if (m_Mixer) m_Mixer->setVerboseLevel( l );
343     GenericAVC::AvDevice::setVerboseLevel( l );
344     debugOutput( DEBUG_LEVEL_VERBOSE, "Setting verbose level to %d...\n", l );
345 }
346
347 AVC::Subunit*
348 AvDevice::createSubunit(AVC::Unit& unit,
349                         AVC::ESubunitType type,
350                         AVC::subunit_t id )
351 {
352     AVC::Subunit* s=NULL;
353     switch (type) {
354         case eST_Audio:
355             s=new BeBoB::SubunitAudio(unit, id );
356             break;
357         case eST_Music:
358             s=new BeBoB::SubunitMusic(unit, id );
359             break;
360         default:
361             s=NULL;
362             break;
363     }
364     if(s) s->setVerboseLevel(getDebugLevel());
365     return s;
366 }
367
368
369 AVC::Plug *
370 AvDevice::createPlug( AVC::Unit* unit,
371                       AVC::Subunit* subunit,
372                       AVC::function_block_type_t functionBlockType,
373                       AVC::function_block_type_t functionBlockId,
374                       AVC::Plug::EPlugAddressType plugAddressType,
375                       AVC::Plug::EPlugDirection plugDirection,
376                       AVC::plug_id_t plugId )
377 {
378
379     Plug *p= new BeBoB::Plug( unit,
380                               subunit,
381                               functionBlockType,
382                               functionBlockId,
383                               plugAddressType,
384                               plugDirection,
385                               plugId );
386     if (p) p->setVerboseLevel(getDebugLevel());
387     return p;
388 }
389
390 bool
391 AvDevice::propagatePlugInfo() {
392     // we don't have to propagate since we discover things
393     // another way
394     debugOutput(DEBUG_LEVEL_VERBOSE, "Skip plug info propagation\n");
395     return true;
396 }
397
398
399 uint8_t
400 AvDevice::getConfigurationIdSampleRate()
401 {
402     ExtendedStreamFormatCmd extStreamFormatCmd( get1394Service() );
403     UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR, 0 );
404     extStreamFormatCmd.setPlugAddress( PlugAddress( PlugAddress::ePD_Input,
405                                                     PlugAddress::ePAM_Unit,
406                                                     unitPlugAddress ) );
407
408     extStreamFormatCmd.setNodeId( getNodeId() );
409     extStreamFormatCmd.setCommandType( AVCCommand::eCT_Status );
410     extStreamFormatCmd.setVerbose( getDebugLevel() );
411
412     if ( !extStreamFormatCmd.fire() ) {
413         debugError( "Stream format command failed\n" );
414         return false;
415     }
416
417     FormatInformation* formatInfo =
418         extStreamFormatCmd.getFormatInformation();
419     FormatInformationStreamsCompound* compoundStream
420         = dynamic_cast< FormatInformationStreamsCompound* > (
421             formatInfo->m_streams );
422     if ( compoundStream ) {
423         debugOutput(DEBUG_LEVEL_VERBOSE, "Sample rate 0x%02x\n",
424                     compoundStream->m_samplingFrequency );
425         return compoundStream->m_samplingFrequency;
426     }
427
428     debugError( "Could not retrieve sample rate\n" );
429     return 0;
430 }
431
432 uint8_t
433 AvDevice::getConfigurationIdNumberOfChannel( PlugAddress::EPlugDirection ePlugDirection )
434 {
435     ExtendedPlugInfoCmd extPlugInfoCmd( get1394Service() );
436     UnitPlugAddress unitPlugAddress( UnitPlugAddress::ePT_PCR,
437                                      getNodeId() );
438     extPlugInfoCmd.setPlugAddress( PlugAddress( ePlugDirection,
439                                                 PlugAddress::ePAM_Unit,
440                                                 unitPlugAddress ) );
441     extPlugInfoCmd.setNodeId( getNodeId() );
442     extPlugInfoCmd.setCommandType( AVCCommand::eCT_Status );
443     extPlugInfoCmd.setVerbose( getDebugLevel() );
444     ExtendedPlugInfoInfoType extendedPlugInfoInfoType(
445         ExtendedPlugInfoInfoType::eIT_NoOfChannels );
446     extendedPlugInfoInfoType.initialize();
447     extPlugInfoCmd.setInfoType( extendedPlugInfoInfoType );
448
449     if ( !extPlugInfoCmd.fire() ) {
450         debugError( "Number of channels command failed\n" );
451         return false;
452     }
453
454     ExtendedPlugInfoInfoType* infoType = extPlugInfoCmd.getInfoType();
455     if ( infoType
456          && infoType->m_plugNrOfChns )
457     {
458         debugOutput(DEBUG_LEVEL_VERBOSE, "Number of channels 0x%02x\n",
459                     infoType->m_plugNrOfChns->m_nrOfChannels );
460         return infoType->m_plugNrOfChns->m_nrOfChannels;
461     }
462
463     debugError( "Could not retrieve number of channels\n" );
464     return 0;
465 }
466
467 uint8_t
468 AvDevice::getConfigurationIdSyncMode()
469 {
470     SignalSourceCmd signalSourceCmd( get1394Service() );
471     SignalUnitAddress signalUnitAddr;
472     signalUnitAddr.m_plugId = 0x01;
473     signalSourceCmd.setSignalDestination( signalUnitAddr );
474     signalSourceCmd.setNodeId( getNodeId() );
475     signalSourceCmd.setSubunitType( eST_Unit  );
476     signalSourceCmd.setSubunitId( 0xff );
477     signalSourceCmd.setVerbose( getDebugLevel() );
478
479     signalSourceCmd.setCommandType( AVCCommand::eCT_Status );
480
481     if ( !signalSourceCmd.fire() ) {
482         debugError( "Signal source command failed\n" );
483         return false;
484     }
485
486     SignalAddress* pSyncPlugSignalAddress = signalSourceCmd.getSignalSource();
487     SignalSubunitAddress* pSyncPlugSubunitAddress
488         = dynamic_cast<SignalSubunitAddress*>( pSyncPlugSignalAddress );
489     if ( pSyncPlugSubunitAddress ) {
490         debugOutput(DEBUG_LEVEL_VERBOSE, "Sync mode 0x%02x\n",
491                     ( pSyncPlugSubunitAddress->m_subunitType << 3
492                       | pSyncPlugSubunitAddress->m_subunitId ) << 8
493                     | pSyncPlugSubunitAddress->m_plugId );
494
495         return ( pSyncPlugSubunitAddress->m_subunitType << 3
496                  | pSyncPlugSubunitAddress->m_subunitId ) << 8
497             | pSyncPlugSubunitAddress->m_plugId;
498     }
499
500     SignalUnitAddress* pSyncPlugUnitAddress
501       = dynamic_cast<SignalUnitAddress*>( pSyncPlugSignalAddress );
502     if ( pSyncPlugUnitAddress ) {
503         debugOutput(DEBUG_LEVEL_VERBOSE, "Sync mode 0x%02x\n",
504                       0xff << 8 | pSyncPlugUnitAddress->m_plugId );
505
506         return ( 0xff << 8 | pSyncPlugUnitAddress->m_plugId );
507     }
508
509     debugError( "Could not retrieve sync mode\n" );
510     return 0;
511 }
512
513 uint64_t
514 AvDevice::getConfigurationId()
515 {
516     // create a unique configuration id.
517     uint64_t id = 0;
518     id = getConfigurationIdSampleRate();
519     id |= ( getConfigurationIdNumberOfChannel( PlugAddress::ePD_Input )
520             + getConfigurationIdNumberOfChannel( PlugAddress::ePD_Output ) ) << 8;
521     id |= getConfigurationIdSyncMode() << 16;
522
523     return id;
524 }
525
526 bool
527 AvDevice::serialize( std::string basePath,
528                      Util::IOSerialize& ser ) const
529 {
530     bool result;
531     result  = GenericAVC::AvDevice::serialize( basePath, ser );
532     return result;
533 }
534
535 bool
536 AvDevice::deserialize( std::string basePath,
537                        Util::IODeserialize& deser )
538 {
539     bool result;
540     result  = GenericAVC::AvDevice::deserialize( basePath, deser );
541     return result;
542 }
543
544 std::string
545 AvDevice::getCachePath()
546 {
547     std::string cachePath;
548     char* pCachePath;
549
550     string path = CACHEDIR;
551     if ( path.size() && path[0] == '~' ) {
552         path.erase( 0, 1 ); // remove ~
553         path.insert( 0, getenv( "HOME" ) ); // prepend the home path
554     }
555
556     if ( asprintf( &pCachePath, "%s/cache/",  path.c_str() ) < 0 ) {
557         debugError( "Could not create path string for cache pool (trying '/var/cache/libffado' instead)\n" );
558         cachePath == "/var/cache/libffado/";
559     } else {
560         cachePath = pCachePath;
561         free( pCachePath );
562     }
563     return cachePath;
564 }
565
566 bool
567 AvDevice::loadFromCache()
568 {
569     std::string sDevicePath = getCachePath() + getConfigRom().getGuidString();
570
571     char* configId;
572     asprintf(&configId, "%016llx", getConfigurationId() );
573     if ( !configId ) {
574         debugError( "could not create id string\n" );
575         return false;
576     }
577
578     std::string sFileName = sDevicePath + "/" + configId + ".xml";
579     free( configId );
580     debugOutput( DEBUG_LEVEL_NORMAL, "filename %s\n", sFileName.c_str() );
581
582     struct stat buf;
583     if ( stat( sFileName.c_str(), &buf ) != 0 ) {
584         debugOutput( DEBUG_LEVEL_NORMAL,  "\"%s\" does not exist\n",  sFileName.c_str() );
585         return false;
586     } else {
587         if ( !S_ISREG( buf.st_mode ) ) {
588             debugOutput( DEBUG_LEVEL_NORMAL,  "\"%s\" is not a regular file\n",  sFileName.c_str() );
589             return false;
590         }
591     }
592
593     Util::XMLDeserialize deser( sFileName, getDebugLevel() );
594
595     if (!deser.isValid()) {
596         debugOutput( DEBUG_LEVEL_NORMAL, "cache not valid: %s\n",
597                      sFileName.c_str() );
598         return false;
599     }
600
601     bool result = deserialize( "", deser );
602     if ( result ) {
603         debugOutput( DEBUG_LEVEL_NORMAL, "could create valid bebob driver from %s\n",
604                      sFileName.c_str() );
605     }
606
607     if(result) {
608         buildMixer();
609     }
610
611     return result;
612 }
613
614 bool
615 AvDevice::saveCache()
616 {
617     // the path looks like this:
618     // PATH_TO_CACHE + GUID + CONFIGURATION_ID
619     string tmp_path = getCachePath() + getConfigRom().getGuidString();
620
621     // the following piece should do something like
622     // 'mkdir -p some/path/with/some/dirs/which/do/not/exist'
623     vector<string> tokens;
624     tokenize( tmp_path, tokens, "/" );
625     string path;
626     for ( vector<string>::const_iterator it = tokens.begin();
627           it != tokens.end();
628           ++it )
629     {
630         path +=  "/" + *it;
631
632         struct stat buf;
633         if ( stat( path.c_str(), &buf ) == 0 ) {
634             if ( !S_ISDIR( buf.st_mode ) ) {
635                 debugError( "\"%s\" is not a directory\n",  path.c_str() );
636                 return false;
637             }
638         } else {
639             if (  mkdir( path.c_str(), S_IRWXU | S_IRWXG ) != 0 ) {
640                 debugError( "Could not create \"%s\" directory\n", path.c_str() );
641                 return false;
642             }
643         }
644     }
645
646     // come up with an unique file name for the current settings
647     char* configId;
648     asprintf(&configId, "%016llx", BeBoB::AvDevice::getConfigurationId() );
649     if ( !configId ) {
650         debugError( "Could not create id string\n" );
651         return false;
652     }
653     string filename = path + "/" + configId + ".xml";
654     free( configId );
655     debugOutput( DEBUG_LEVEL_NORMAL, "filename %s\n", filename.c_str() );
656
657     Util::XMLSerialize ser( filename );
658     return serialize( "", ser );
659 }
660
661 } // end of namespace
Note: See TracBrowser for help on using the browser.