root/branches/streaming-rework/src/libfreebobavc/ieee1394service.cpp

Revision 412, 20.8 kB (checked in by pieterpalmers, 16 years ago)

- added some documentation
- added a lock()/unlock() to IAvDevice (see header)
- reimplemented test-freebob to the new C++ api
- started with support for AddressRangeMapping?, i.e. response

to reads/writes of the 1394 memory space on the host

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /* ieee1394service.cpp
2  * Copyright (C) 2005,07 by Daniel Wagner
3  * Copyright (C) 2007 by Pieter Palmers
4  *
5  * This file is part of FreeBoB.
6  *
7  * FreeBoB is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  * FreeBoB is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with FreeBoB; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19  * MA 02111-1307 USA.
20  */
21 #include "ieee1394service.h"
22
23 #include <libavc1394/avc1394.h>
24 #include <libraw1394/csr.h>
25 #include <libiec61883/iec61883.h>
26
27 #include <errno.h>
28 #include <netinet/in.h>
29
30 #include <string.h>
31
32 #include <iostream>
33 #include <iomanip>
34
35 IMPL_DEBUG_MODULE( Ieee1394Service, Ieee1394Service, DEBUG_LEVEL_VERBOSE );
36
37 Ieee1394Service::Ieee1394Service()
38     : m_handle( 0 ), m_resetHandle( 0 )
39     , m_port( -1 )
40     , m_threadRunning( false )
41 {
42     pthread_mutex_init( &m_mutex, 0 );
43    
44     for (unsigned int i=0; i<64; i++) {
45         m_channels[i].channel=-1;
46         m_channels[i].bandwidth=-1;
47         m_channels[i].alloctype=AllocFree;
48         m_channels[i].xmit_node=0xFFFF;
49         m_channels[i].xmit_plug=-1;
50         m_channels[i].recv_node=0xFFFF;
51         m_channels[i].recv_plug=-1;
52     }
53 }
54
55 Ieee1394Service::~Ieee1394Service()
56 {
57     stopRHThread();
58
59     for ( arm_mapping_vec_t::iterator it = m_arm_mappings.begin();
60           it != m_arm_mappings.end();
61           ++it )
62     {
63         debugOutput(DEBUG_LEVEL_VERBOSE, "Unregistering ARM handler for 0x%016llX\n", (*it)->start);
64         int err=raw1394_arm_unregister(m_resetHandle, (*it)->start);
65         if (err) {
66             debugError(" Failed to unregister ARM handler for 0x%016llX\n", (*it)->start);
67             debugError(" Error: %s\n", strerror(errno));
68         } else {
69             delete *it;
70         }
71     }
72
73     if ( m_handle ) {
74         raw1394_destroy_handle( m_handle );
75     }
76    
77     if ( m_resetHandle ) {
78         raw1394_destroy_handle( m_resetHandle );
79     }
80 }
81
82 bool
83 Ieee1394Service::initialize( int port )
84 {
85     using namespace std;
86
87     m_handle = raw1394_new_handle_on_port( port );
88     if ( !m_handle ) {
89         if ( !errno ) {
90             debugFatal("libraw1394 not compatible\n");
91         } else {
92             debugFatal("Ieee1394Service::initialize: Could not get 1394 handle: %s\n",
93                 strerror(errno) );
94             debugFatal("Is ieee1394 and raw1394 driver loaded?\n");
95         }
96         return false;
97     }
98
99     m_resetHandle = raw1394_new_handle_on_port( port );
100     if ( !m_handle ) {
101         if ( !errno ) {
102             debugFatal("libraw1394 not compatible\n");
103         } else {
104             debugFatal("Ieee1394Service::initialize: Could not get 1394 handle: %s",
105                 strerror(errno) );
106             debugFatal("Is ieee1394 and raw1394 driver loaded?\n");
107         }
108         return false;
109     }
110
111     m_port = port;
112
113     raw1394_set_userdata( m_handle, this );
114     raw1394_set_userdata( m_resetHandle, this );
115     raw1394_set_bus_reset_handler( m_resetHandle,
116                                    this->resetHandlerLowLevel );
117
118     m_default_arm_handler = raw1394_set_arm_tag_handler( m_resetHandle,
119                                    this->armHandlerLowLevel );
120     startRHThread();
121
122     return true;
123 }
124
125 int
126 Ieee1394Service::getNodeCount()
127 {
128     return raw1394_get_nodecount( m_handle );
129 }
130
131 nodeid_t Ieee1394Service::getLocalNodeId() {
132     return raw1394_get_local_id(m_handle) & 0x3F;
133 }
134
135 bool
136 Ieee1394Service::read( fb_nodeid_t nodeId,
137                        fb_nodeaddr_t addr,
138                        size_t length,
139                        fb_quadlet_t* buffer )
140 {
141     using namespace std;
142     if ( raw1394_read( m_handle, nodeId, addr, length*4, buffer ) == 0 ) {
143
144         #ifdef DEBUG
145         debugOutput(DEBUG_LEVEL_VERY_VERBOSE,
146             "read: node 0x%X, addr = 0x%016llX, length = %u\n",
147             nodeId, addr, length);
148         printBuffer( length, buffer );
149         #endif
150
151         return true;
152     } else {
153         #ifdef DEBUG
154         debugError("raw1394_read failed: node 0x%X, addr = 0x%016llX, length = %u\n",
155               nodeId, addr, length);
156         #endif
157
158         return false;
159     }
160 }
161
162
163 bool
164 Ieee1394Service::read_quadlet( fb_nodeid_t nodeId,
165                                fb_nodeaddr_t addr,
166                                fb_quadlet_t* buffer )
167 {
168     return read( nodeId,  addr, sizeof( *buffer )/4, buffer );
169 }
170
171 bool
172 Ieee1394Service::read_octlet( fb_nodeid_t nodeId,
173                               fb_nodeaddr_t addr,
174                               fb_octlet_t* buffer )
175 {
176     return read( nodeId, addr, sizeof( *buffer )/4,
177                  reinterpret_cast<fb_quadlet_t*>( buffer ) );
178 }
179
180
181 bool
182 Ieee1394Service::write( fb_nodeid_t nodeId,
183                         fb_nodeaddr_t addr,
184                         size_t length,
185                         fb_quadlet_t* data )
186 {
187     using namespace std;
188
189     #ifdef DEBUG
190     debugOutput(DEBUG_LEVEL_VERY_VERBOSE,"write: node 0x%X, addr = 0x%016X, length = %d\n",
191                 nodeId, addr, length);
192     printBuffer( length, data );
193     #endif
194
195     return raw1394_write( m_handle, nodeId, addr, length*4, data ) == 0;
196 }
197
198
199 bool
200 Ieee1394Service::write_quadlet( fb_nodeid_t nodeId,
201                                 fb_nodeaddr_t addr,
202                                 fb_quadlet_t data )
203 {
204     return write( nodeId, addr, sizeof( data )/4, &data );
205 }
206
207 bool
208 Ieee1394Service::write_octlet( fb_nodeid_t nodeId,
209                                fb_nodeaddr_t addr,
210                                fb_octlet_t data )
211 {
212     return write( nodeId, addr, sizeof( data )/4,
213                   reinterpret_cast<fb_quadlet_t*>( &data ) );
214 }
215
216 fb_quadlet_t*
217 Ieee1394Service::transactionBlock( fb_nodeid_t nodeId,
218                                    fb_quadlet_t* buf,
219                                    int len,
220                                    unsigned int* resp_len )
221 {
222     for (int i = 0; i < len; ++i) {
223         buf[i] = ntohl( buf[i] );
224     }
225
226     #ifdef DEBUG
227     debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "  pre avc1394_transaction_block2\n" );
228     printBuffer( len, buf );
229     #endif
230
231     fb_quadlet_t* result =
232         avc1394_transaction_block2( m_handle,
233                                     nodeId,
234                                     buf,
235                                     len,
236                                     resp_len,
237                                     10 );
238
239     #ifdef DEBUG
240     debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE, "  post avc1394_transaction_block2\n" );
241     printBuffer( *resp_len, result );
242     #endif
243
244     for ( unsigned int i = 0; i < *resp_len; ++i ) {
245         result[i] = htonl( result[i] );
246     }
247
248     return result;
249 }
250
251
252 bool
253 Ieee1394Service::transactionBlockClose()
254 {
255     avc1394_transaction_block_close( m_handle );
256     return true;
257 }
258
259 bool
260 Ieee1394Service::setVerbose( int verboseLevel )
261 {
262     setDebugLevel(verboseLevel);
263     return true;
264 }
265
266 int
267 Ieee1394Service::getVerboseLevel()
268 {
269     return getDebugLevel();
270 }
271
272 void
273 Ieee1394Service::printBuffer( size_t length, fb_quadlet_t* buffer ) const
274 {
275
276     for ( unsigned int i=0; i < length; ++i ) {
277         if ( ( i % 4 ) == 0 ) {
278             if ( i > 0 ) {
279                 debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE,"\n");
280             }
281             debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE," %4d: ",i*4);
282         }
283         debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE,"%08X ",buffer[i]);
284     }
285     debugOutputShort(DEBUG_LEVEL_VERY_VERBOSE,"\n");
286 }
287
288 int
289 Ieee1394Service::resetHandlerLowLevel( raw1394handle_t handle,
290                                        unsigned int generation )
291 {
292     raw1394_update_generation ( handle, generation );
293     Ieee1394Service* instance
294         = (Ieee1394Service*) raw1394_get_userdata( handle );
295     instance->resetHandler( generation );
296
297     return 0;
298 }
299
300 bool
301 Ieee1394Service::resetHandler( unsigned int generation )
302 {
303     m_generation = generation;
304
305     for ( reset_handler_vec_t::iterator it = m_busResetHandlers.begin();
306           it != m_busResetHandlers.end();
307           ++it )
308     {
309         Functor* func = *it;
310         ( *func )();
311     }
312
313     return true;
314 }
315
316 bool Ieee1394Service::registerARMrange(nodeaddr_t start,
317                      size_t length, byte_t *initial_value,
318                      arm_options_t access_rights,
319                      arm_options_t notification_options,
320                      arm_options_t client_transactions,
321                      Functor* functor) {
322     debugOutput(DEBUG_LEVEL_VERBOSE, "Registering ARM handler for 0x%016llX, length %u\n", start,length);
323                      
324     arm_mapping_t *new_mapping=NULL;
325     new_mapping = new arm_mapping_t;
326    
327     new_mapping->start=start;
328     new_mapping->length=length;
329     new_mapping->access_rights=access_rights;
330     new_mapping->notification_options=notification_options;
331     new_mapping->client_transactions=client_transactions;
332     new_mapping->functor=functor;
333    
334     int err=raw1394_arm_register(m_resetHandle, start,
335                          length, initial_value, (octlet_t)new_mapping,
336                          access_rights,
337                          notification_options,
338                          client_transactions);
339     if (err) {
340         debugError("Failed to register ARM handler for 0x%016llX\n", start);
341         debugError(" Error: %s\n", strerror(errno));
342         delete new_mapping;
343         return false;
344     }
345    
346     m_arm_mappings.push_back( new_mapping );
347
348     return true;
349 }
350
351 bool Ieee1394Service::unregisterARMrange( nodeaddr_t start ) {
352     debugOutput(DEBUG_LEVEL_VERBOSE, "Unregistering ARM handler for 0x%016llX\n", start);
353    
354     for ( arm_mapping_vec_t::iterator it = m_arm_mappings.begin();
355           it != m_arm_mappings.end();
356           ++it )
357     {
358         if((*it)->start == start) {
359             int err=raw1394_arm_unregister(m_resetHandle, start);
360             if (err) {
361                 debugError("Failed to unregister ARM handler for 0x%016llX\n", start);
362                 debugError(" Error: %s\n", strerror(errno));
363             } else {
364                 m_arm_mappings.erase(it);
365                 delete *it;
366                 return true;
367             }
368         }
369     }
370     debugOutput(DEBUG_LEVEL_VERBOSE, " no handler found!\n");
371    
372     return false;
373 }
374    
375 int
376 Ieee1394Service::armHandlerLowLevel(raw1394handle_t handle,
377                      unsigned long arm_tag,
378                      byte_t request_type, unsigned int requested_length,
379                      void *data)
380 {
381     Ieee1394Service* instance
382         = (Ieee1394Service*) raw1394_get_userdata( handle );
383     instance->armHandler( arm_tag, request_type, requested_length, data );
384
385     return 0;
386 }
387
388 bool
389 Ieee1394Service::armHandler(  unsigned long arm_tag,
390                      byte_t request_type, unsigned int requested_length,
391                      void *data)
392 {
393     for ( arm_mapping_vec_t::iterator it = m_arm_mappings.begin();
394           it != m_arm_mappings.end();
395           ++it )
396     {
397         if((*it) == (arm_mapping_t *)arm_tag) {
398             debugOutput(DEBUG_LEVEL_VERBOSE,"ARM handler for address 0x%016llX called\n", (*it)->start);
399             Functor* func = (*it)->functor;
400             ( *func )();
401             return true;
402         }
403     }
404
405     debugOutput(DEBUG_LEVEL_VERBOSE,"default ARM handler called\n");
406
407     m_default_arm_handler(m_resetHandle, arm_tag, request_type, requested_length, data );
408     return true;
409 }
410
411 bool
412 Ieee1394Service::startRHThread()
413 {
414     int i;
415
416     if ( m_threadRunning ) {
417         return true;
418     }
419     pthread_mutex_lock( &m_mutex );
420     i = pthread_create( &m_thread, 0, rHThread, this );
421     pthread_mutex_unlock( &m_mutex );
422     if (i) {
423         debugFatal("Could not start ieee1394 service thread\n");
424         return false;
425     }
426     m_threadRunning = true;
427
428     return true;
429 }
430
431 void
432 Ieee1394Service::stopRHThread()
433 {
434     if ( m_threadRunning ) {
435         pthread_mutex_lock (&m_mutex);
436         pthread_cancel (m_thread);
437         pthread_join (m_thread, 0);
438         pthread_mutex_unlock (&m_mutex);
439         m_threadRunning = false;
440     }
441 }
442
443 void*
444 Ieee1394Service::rHThread( void* arg )
445 {
446     Ieee1394Service* pIeee1394Service = (Ieee1394Service*) arg;
447
448     while (true) {
449         raw1394_loop_iterate (pIeee1394Service->m_resetHandle);
450         pthread_testcancel ();
451     }
452
453     return 0;
454 }
455
456 bool
457 Ieee1394Service::addBusResetHandler( Functor* functor )
458 {
459     debugOutput(DEBUG_LEVEL_VERBOSE, "Adding busreset handler (%p)\n", functor);
460     m_busResetHandlers.push_back( functor );
461     return true;
462 }
463
464 bool
465 Ieee1394Service::remBusResetHandler( Functor* functor )
466 {
467     debugOutput(DEBUG_LEVEL_VERBOSE, "Removing busreset handler (%p)\n", functor);
468    
469     for ( reset_handler_vec_t::iterator it = m_busResetHandlers.begin();
470           it != m_busResetHandlers.end();
471           ++it )
472     {
473         if ( *it == functor ) {
474             debugOutput(DEBUG_LEVEL_VERBOSE, " found\n");
475             m_busResetHandlers.erase( it );
476             return true;
477         }
478     }
479     debugOutput(DEBUG_LEVEL_VERBOSE, " not found\n");
480     return false;
481 }
482
483 /**
484  * Allocates an iso channel for use by the interface in a similar way to
485  * libiec61883.  Returns -1 on error (due to there being no free channels)
486  * or an allocated channel number.
487  *
488  * Does not perform anything other than registering the channel and the
489  * bandwidth at the IRM
490  *
491  * Also allocates the necessary bandwidth (in ISO allocation units).
492  *
493  * FIXME: As in libiec61883, channel 63 is not requested; this is either a
494  * bug or it's omitted since that's the channel preferred by video devices.
495  *
496  * @param bandwidth the bandwidth to allocate for this channel
497  * @return the channel number
498  */
499 signed int Ieee1394Service::allocateIsoChannelGeneric(unsigned int bandwidth) {
500     debugOutput(DEBUG_LEVEL_VERBOSE, "Allocating ISO channel using generic method...\n" );
501    
502     struct ChannelInfo cinfo;
503
504     int c = -1;
505     for (c = 0; c < 63; c++) {
506         if (raw1394_channel_modify (m_handle, c, RAW1394_MODIFY_ALLOC) == 0)
507             break;
508     }
509     if (c < 63) {
510         if (raw1394_bandwidth_modify(m_handle, bandwidth, RAW1394_MODIFY_ALLOC) < 0) {
511             debugFatal("Could not allocate bandwidth of %d\n", bandwidth);
512            
513             raw1394_channel_modify (m_handle, c, RAW1394_MODIFY_FREE);
514             return -1;
515         } else {
516             cinfo.channel=c;
517             cinfo.bandwidth=bandwidth;
518             cinfo.alloctype=AllocGeneric;
519            
520             if (registerIsoChannel(c, cinfo)) {
521                 return c;
522             } else {
523                 raw1394_bandwidth_modify(m_handle, bandwidth, RAW1394_MODIFY_FREE);
524                 raw1394_channel_modify (m_handle, c, RAW1394_MODIFY_FREE);
525                 return -1;
526             }
527         }
528     }
529     return -1;
530 }
531
532 /**
533  * Allocates an iso channel for use by the interface in a similar way to
534  * libiec61883.  Returns -1 on error (due to there being no free channels)
535  * or an allocated channel number.
536  *
537  * Uses IEC61883 Connection Management Procedure to establish the connection.
538  *
539  * Also allocates the necessary bandwidth (in ISO allocation units).
540  *
541  * @param xmit_node  node id of the transmitter
542  * @param xmit_plug  the output plug to use. If -1, find the first online plug, and
543  * upon return, contains the plug number used.
544  * @param recv_node  node id of the receiver
545  * @param recv_plug the input plug to use. If -1, find the first online plug, and
546  * upon return, contains the plug number used.
547  *
548  * @return the channel number
549  */
550
551 signed int Ieee1394Service::allocateIsoChannelCMP(
552     nodeid_t xmit_node, int xmit_plug,
553     nodeid_t recv_node, int recv_plug
554     ) {
555
556     debugOutput(DEBUG_LEVEL_VERBOSE, "Allocating ISO channel using IEC61883 CMP...\n" );
557    
558     struct ChannelInfo cinfo;
559    
560     int c = -1;
561     int bandwidth=1;
562    
563     // do connection management: make connection
564     c = iec61883_cmp_connect(
565         m_handle,
566         xmit_node | 0xffc0,
567         &xmit_plug,
568         recv_node | 0xffc0,
569         &recv_plug,
570         &bandwidth);
571
572     if((c<0) || (c>63)) {
573         debugError("Could not do CMP from %04X:%02d to %04X:%02d\n",
574             xmit_node, xmit_plug, recv_node, recv_plug
575             );
576         return -1;
577     }
578
579     cinfo.channel=c;
580     cinfo.bandwidth=bandwidth;
581     cinfo.alloctype=AllocCMP;
582    
583     cinfo.xmit_node=xmit_node;
584     cinfo.xmit_plug=xmit_plug;
585     cinfo.recv_node=recv_node;
586     cinfo.recv_plug=recv_plug;
587        
588     if (registerIsoChannel(c, cinfo)) {
589         return c;
590     }
591
592     return -1;
593 }
594
595 /**
596  * Deallocates an iso channel.  Silently ignores a request to deallocate
597  * a negative channel number.
598  *
599  * Figures out the method that was used to allocate the channel (generic, cmp, ...)
600  * and uses the appropriate method to deallocate. Also frees the bandwidth
601  * that was reserved along with this channel.
602  *
603  * @param c channel number
604  * @return true if successful
605  */
606 bool Ieee1394Service::freeIsoChannel(signed int c) {
607     debugOutput(DEBUG_LEVEL_VERBOSE, "Freeing ISO channel %d...\n", c );
608    
609     if (c < 0 || c > 63) {
610         debugWarning("Invalid channel number: %d\n", c);
611         return false;
612     }
613    
614     switch (m_channels[c].alloctype) {
615         default:
616             debugError(" BUG: invalid allocation type!\n");
617             return false;
618            
619         case AllocFree:
620             debugWarning(" Channel %d not registered\n", c);
621             return false;
622            
623         case AllocGeneric:
624             debugOutput(DEBUG_LEVEL_VERBOSE, " allocated using generic routine...\n" );
625             if (unregisterIsoChannel(c)) {
626                 return false;
627             } else {
628                 debugOutput(DEBUG_LEVEL_VERBOSE, " freeing %d bandwidth units...\n", m_channels[c].bandwidth );
629                 if (raw1394_bandwidth_modify(m_handle, m_channels[c].bandwidth, RAW1394_MODIFY_FREE) !=0) {
630                     debugWarning("Failed to deallocate bandwidth\n");
631                 }
632                 debugOutput(DEBUG_LEVEL_VERBOSE, " freeing channel %d...\n", m_channels[c].channel );
633                 if (raw1394_channel_modify (m_handle, m_channels[c].channel, RAW1394_MODIFY_FREE) != 0) {
634                     debugWarning("Failed to free channel\n");
635                 }
636                 return true;
637             }
638            
639         case AllocCMP:
640             debugOutput(DEBUG_LEVEL_VERBOSE, " allocated using IEC61883 CMP...\n" );
641             if (unregisterIsoChannel(c)) {
642                 return false;
643             } else {
644                 debugOutput(DEBUG_LEVEL_VERBOSE, " performing IEC61883 CMP disconnect...\n" );
645                 if(iec61883_cmp_disconnect(
646                         m_handle,
647                         m_channels[c].xmit_node | 0xffc0,
648                         m_channels[c].xmit_plug,
649                         m_channels[c].recv_node | 0xffc0,
650                         m_channels[c].recv_plug,
651                         m_channels[c].channel,
652                         m_channels[c].bandwidth) != 0) {
653                     debugWarning("Could not do CMP disconnect for channel %d!\n",c);
654                 }
655             }
656             return true;
657     }
658    
659     // unreachable
660     debugError("BUG: unreachable code reached!\n");
661    
662     return false;
663 }
664
665 /**
666  * Registers a channel as managed by this ieee1394service
667  * @param c channel number
668  * @param cinfo channel info struct
669  * @return true if successful
670  */
671 bool Ieee1394Service::registerIsoChannel(unsigned int c, struct ChannelInfo cinfo) {
672     if (c < 63) {
673         if (m_channels[c].alloctype != AllocFree) {
674             debugWarning("Channel %d already registered with bandwidth %d\n",
675                 m_channels[c].channel, m_channels[c].bandwidth);
676         }
677        
678         memcpy(&m_channels[c], &cinfo, sizeof(struct ChannelInfo));
679        
680     } else return false;
681     return true;
682 }
683
684 /**
685  * unegisters a channel from this ieee1394service
686  * @param c channel number
687  * @return true if successful
688  */
689 bool Ieee1394Service::unregisterIsoChannel(unsigned int c) {
690     if (c < 63) {
691         if (m_channels[c].alloctype == AllocFree) {
692             debugWarning("Channel %d not registered\n", c);
693             return false;
694         }
695        
696         m_channels[c].channel=-1;
697         m_channels[c].bandwidth=-1;
698         m_channels[c].alloctype=AllocFree;
699         m_channels[c].xmit_node=0xFFFF;
700         m_channels[c].xmit_plug=-1;
701         m_channels[c].recv_node=0xFFFF;
702         m_channels[c].recv_plug=-1;
703        
704     } else return false;
705     return true;
706 }
707
708 /**
709  * Returns the current value of the `bandwidth available' register on
710  * the IRM, or -1 on error.
711  * @return
712  */
713 signed int Ieee1394Service::getAvailableBandwidth() {
714     quadlet_t buffer;
715     signed int result = raw1394_read (m_handle, raw1394_get_irm_id (m_handle),
716         CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE,
717         sizeof (quadlet_t), &buffer);
718
719     if (result < 0)
720         return -1;
721     return ntohl(buffer);
722 }
Note: See TracBrowser for help on using the browser.