File Coverage

blib/lib/Solaris/DeviceTree/Libdevinfo.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             #
2             # $Header: /cvsroot/devicetool/Solaris-DeviceTree/lib/Solaris/DeviceTree/Libdevinfo.pm,v 1.9 2003/12/12 11:11:55 honkbude Exp $
3             #
4              
5             package Solaris::DeviceTree::Libdevinfo;
6              
7 1     1   966 use 5.006;
  1         3  
  1         46  
8 1     1   6 use strict;
  1         2  
  1         39  
9 1     1   17 use warnings;
  1         2  
  1         35  
10 1     1   6 use Carp;
  1         9  
  1         94  
11 1     1   1872 use English;
  1         2588  
  1         10  
12              
13             our @ISA = qw( Solaris::DeviceTree::Node );
14             our $VERSION = do { my @r = (q$Revision: 1.9 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker
15              
16 1     1   1262 use Solaris::DeviceTree::Libdevinfo::Impl;
  0            
  0            
17             use Solaris::DeviceTree::Libdevinfo::MinorNode;
18             use Solaris::DeviceTree::Libdevinfo::Property;
19             use Solaris::DeviceTree::Libdevinfo::PromProperty;
20              
21             # Package global containing a reference to the Libdevinfo tree (singleton)
22             our $_ROOT_NODE;
23              
24             # Package global containing a reference to the system PROM handle (singleton)
25             our $_PROM_HANDLE;
26              
27             =pod
28              
29             =head1 NAME
30              
31             Solaris::DeviceTree::Libdevinfo - Perl interface to the Solaris devinfo library
32              
33             =head1 SYNOPSIS
34              
35             Construction and destruction:
36              
37             use Solaris::DeviceTree::Libdevinfo;
38             $tree = Solaris::DeviceTree::Libdevinfo->new;
39              
40             Data access methods:
41              
42             $path = $node->devfs_path;
43             $nodename = $node->node_name;
44             $bindingname = $node->binding_name;
45             $busaddr = $node->bus_addr;
46             @cnames = $devtree->compatible_names;
47             $drivername = $devtree->driver_name;
48             %ops = $devtree->driver_ops;
49             $inst = $node->instance;
50             %state = $node->state;
51             $id = $node->nodeid;
52             if( $node->is_pseudo_node ) { ... }
53             if( $node->is_sid_node ) { ... }
54             if( $node->is_prom_node ) { ... }
55             $props = $node->props;
56             $promprops = $node->prom_props;
57             @minor = $node->minor_nodes;
58              
59             =head1 DESCRIPTION
60              
61             This module implements the L interface
62             and allows access to the Solaris devinfo library L.
63             The devicetree is represented as a hierarchical collection of nodes
64             in the kernel.
65              
66             The implementation closely resembles the API of the C library. However,
67             due to the object interface and the beauty of Perl there a few differences to keep in mind
68             when using this library after reading the manual pages of the original
69             L:
70              
71             =over 4
72              
73             =item *
74              
75             The 'di_'-prefix of the function names from the C API has been stripped.
76              
77             =item *
78              
79             The functions C and C for generation and destruction of
80             devicetrees are now called implicitly during contruction and destruction repectively.
81              
82             =item *
83              
84             Accessing the nodes by driver via C and C
85             is not implemented in favor of the much more expressive C
86             added in Perl.
87              
88             =item *
89              
90             The function C is not implemented because treewalking
91             in Perl using C is much easier than in C and is therefore
92             not needed.
93              
94             =item *
95              
96             Getting child nodes via subsequent calls to C has been
97             simplified to a single call to C returning an array of
98             all child nodes.
99              
100             =item *
101              
102             Access to C is currently not implemented as the returned
103             value is meaningless without access to L, which I have not done (yet).
104             Requests welcome.
105              
106             =back
107              
108             di_binding_name di_bus_addr
109             di_child_node di_compatible_names
110             di_devfs_path di_devfs_path_free
111             di_devid di_driver_name
112             di_driver_ops di_drv_first_node
113             di_drv_next_node di_fini
114             di_init di_instance
115             di_minor_class di_minor_devt
116             di_minor_name di_minor_next
117             di_minor_nodetype di_minor_spectype
118             di_minor_type di_node_name
119             di_nodeid di_parent_node
120             di_prom_fini di_prom_init
121             di_prom_prop_data di_prom_prop_lookup_bytes
122             di_prom_prop_lookup_ints di_prom_prop_lookup_strings
123             di_prom_prop_name di_prom_prop_next
124             di_prop_bytes di_prop_devt
125             di_prop_ints di_prop_lookup_bytes
126             di_prop_lookup_ints di_prop_lookup_strings
127             di_prop_name di_prop_next
128             di_prop_strings di_prop_type
129             di_sibling_node di_state
130             di_walk_minor di_walk_node
131              
132             =head1 METHODS
133              
134             For tree traversal methods see the base class L.
135              
136             The following methods are available:
137              
138             =head2 new
139              
140             The constructor returns a reference to the root node object, which is a
141             L object.
142             The methods are all read-only.
143              
144             =cut
145              
146             sub new {
147             my ($pkg, %params) = @_;
148              
149             # We always want to access all information from the complete tree.
150             # If only a subset of information is needed we handle it on the
151             # perl end. This might be a performance issue when lots of trees
152             # are generated, but as the methods are all read-only a singleton
153             # tree should be sufficient.
154              
155             if( !defined $_ROOT_NODE ) {
156             $_ROOT_NODE = bless {
157             _data => di_init( "/", $DINFOCPYALL ),
158             _parent => undef,
159             }, $pkg;
160             }
161             return $_ROOT_NODE;
162             }
163              
164             # Special constructor for internal nodes
165             sub _new_internal {
166             my ($pkg, %params) = @_;
167              
168             # The parameter 'data' has the SWIG-type di_node_t and points to
169             # the C data structure needed to access the node from the C library.
170             # The parameter 'parent' points to the parent Perl object of this
171             # node in the device tree. This is done in favor of using di_parent_node
172             # from the library for two reasons: first it's a lot easier, second
173             # it is good to have at most one object per node from the devicetree.
174             # Checking the identity of a node can than be done by comparing the
175             # references.
176             # Both parameters should only be used when nodes inside the tree
177             # are created from within methods of this class.
178              
179             die "No data specified." if( !defined $params{data} );
180             die "No parent specified." if( !defined $params{parent} );
181              
182             my $this = bless {
183             _data => $params{data},
184             _parent => $params{parent},
185             }, $pkg;
186              
187             return $this;
188             }
189              
190             # This helper function generates a persistent prom handle on demand
191             # and returns it.
192             sub _prom_handle {
193             if( !defined $_PROM_HANDLE ) {
194             $_PROM_HANDLE = di_prom_init();
195             if( isDI_PROM_HANDLE_NIL( $_PROM_HANDLE ) ) {
196             # Maybe an exception should be thrown here.
197             # warn "Cannot access PROM device: $ERRNO";
198             $_PROM_HANDLE = undef;
199             }
200             }
201             return $_PROM_HANDLE;
202             }
203              
204             #=pod
205             #
206             #=head3 $tree->DESTROY;
207             #
208             #This is the destructor method. It should not be necessary to
209             #call this method directly.
210             #
211             #This is the equivalent of calling C from the C API.
212             #
213             #=cut
214              
215             sub DESTROY {
216             my $this = shift;
217              
218             # We need weak references for singletons. Fix this some time...
219             if( !defined $this->{_parent} ) {
220             di_prom_fini( $this->{_prom_handle} ) if( defined $this->{_prom_handle} );
221             $this->{_prom_handle} = undef;
222             di_fini( $this->{_data} ) if( defined $this->{_data} );
223             $this->{_data} = undef;
224             }
225             }
226              
227             # tree traversal documented in Solaris::DeviceTree::Node
228             sub child_nodes {
229             my ($this, %options) = @_;
230              
231             # The children of each node are cached
232             if( !exists $this->{_children} ) {
233             # Cache is empty, fill it.
234             my @result = ();
235             my $child = di_child_node( $this->{_data} );
236              
237             # Iterate over all children and generate objects accordlingly
238             while( !isDI_NODE_NIL( $child ) ) {
239             push @result, Solaris::DeviceTree::Libdevinfo->_new_internal(
240             data => $child, parent => $this );
241             $child = di_sibling_node( $child );
242             }
243              
244             # Store result in cache
245             $this->{_children} = \@result;
246             }
247              
248             # Always return contents of cache
249             return @{$this->{_children}};
250             }
251              
252             # tree traversal documented in Solaris::DeviceTree::Node
253             sub parent_node {
254             my $this = shift;
255              
256             # We directly return the parent node. Especially we don't use
257             # di_parent_node from the C library. See the description of
258             # the constructor for the reason.
259             return $this->{_parent};
260             }
261              
262             # tree traversal documented in Solaris::DeviceTree::Node
263             sub root_node {
264             my $this = shift;
265              
266             # Since we have a singleton the same reference to the object is
267             # always returned.
268             return $_ROOT_NODE;
269             }
270              
271             # tree traversal documented in Solaris::DeviceTree::Node
272             sub sibling_nodes {
273             my $this = shift;
274              
275             my $parent = $this->parent_node;
276              
277             # Read all siblings including $this
278             my @siblings = defined $parent ? $parent->child_nodes : ();
279              
280             # Strip out current node
281             my @sib = grep { $_ ne $this } @siblings;
282              
283             return @sib;
284             }
285              
286             =pod
287              
288             =head2 devfs_path
289              
290             Returns the physical path assocatiated with this node.
291              
292             =cut
293              
294             sub devfs_path {
295             my $this = shift;
296             return di_devfs_path( $this->{_data} );
297             }
298              
299             =pod
300              
301             =head2 node_name
302              
303             Returns the name of the node.
304              
305             =cut
306              
307             sub node_name {
308             my $this = shift;
309             return di_node_name( $this->{_data} );
310             }
311              
312             =pod
313              
314             =head2 binding_name
315              
316             Returns the binding name for this node. The binding name
317             is the name used by the system to select a driver for the device.
318              
319             =cut
320              
321             sub binding_name {
322             my $this = shift;
323             return di_binding_name( $this->{_data} );
324             }
325              
326             =pod
327              
328             =head2 bus_addr
329              
330             Returns the address on the bus for this node. C is returned
331             if a bus address has not been assigned to the device. A zero-length
332             string may be returned and is considered a valid bus address.
333              
334             =cut
335              
336             sub bus_addr {
337             my $this = shift;
338             my $busaddr = di_bus_addr( $this->{_data} );
339             return $busaddr;
340             }
341              
342             =pod
343              
344             =head2 compatible_names
345              
346             Returns the list of names from compatible device for the current node.
347             See the discussion of generic names in L for
348             a description of how compatible names are used by Solaris to achieve
349             driver binding for the node.
350              
351             =cut
352              
353             sub compatible_names {
354             my $this = shift;
355             my $node = $this->{_data};
356              
357             my $namehandle = newStringHandle();
358             my $lastIndex = di_compatible_names( $node, $namehandle ) - 1;
359             my @compatibleNames =
360             map { getIndexedString( $namehandle, $_ ) } 0..$lastIndex;
361             freeStringHandle( $namehandle );
362              
363             @compatibleNames;
364             }
365              
366             #sub devid {
367             # my $this = shift;
368             # my $devid = di_devid( $this->{_data} );
369             # return (isDevidNull( $devid ) == 0 ? $devid : 0);
370             #}
371              
372             =pod
373              
374             =head2 driver_name
375              
376             Returns the name of the driver for the node or C if the node
377             is not bound to any driver.
378              
379             =cut
380              
381             sub driver_name {
382             my $this = shift;
383             return di_driver_name( $this->{_data} );
384             }
385              
386             =pod
387              
388             =head2 driver_ops
389              
390             Returns a hash whos keys indicate, which entry points of the
391             device driver entry points are supported by the driver bound
392             to this node. Possible keys are:
393              
394             BUS
395             CB
396             STREAM
397              
398             =cut
399              
400             sub driver_ops {
401             my $this = shift;
402              
403             my $ops = di_driver_ops( $this->{_data} );
404             my %ops;
405              
406             $ops{BUS} = 1 if( $ops & $DI_BUS_OPS );
407             $ops{CB} = 1 if( $ops & $DI_CB_OPS );
408             $ops{STREAM} = 1 if( $ops & $DI_STREAM_OPS );
409             return %ops;
410             }
411              
412             =pod
413              
414             =head2 instance
415              
416             Returns the instance number for this node of the bound driver.
417             C is returned if no instance number has been assigned.
418              
419             =cut
420              
421             sub instance {
422             my $this = shift;
423             my $instance = di_instance( $this->{_data} );
424             # if instance number is -1 then no instance was bound
425             $instance = undef if( $instance == -1 );
426             return $instance;
427             }
428              
429             =pod
430              
431             =head2 state
432              
433             Returns the driver state attached to this node as hash.
434             The presence of the keys in the hash represent the states
435             of the driver. The following keys in the hash can be present:
436              
437             DRIVER_DETACHED
438             DEVICE_OFFLINE
439             DEVICE_DOWN
440             BUS_QUIESCED
441             BUS_DOWN
442              
443             =cut
444              
445             sub state {
446             my $this = shift;
447              
448             my $state = di_state( $this->{_data} );
449             my %state;
450              
451             $state{DRIVER_DETACHED} = 1 if( $state & $DI_DRIVER_DETACHED );
452             $state{DEVICE_OFFLINE} = 1 if( $state & $DI_DEVICE_OFFLINE );
453             $state{DEVICE_DOWN} = 1 if( $state & $DI_DEVICE_DOWN );
454             $state{BUS_QUISCED} = 1 if( $state & $DI_BUS_QUIESCED );
455             $state{BUS_DOWN} = 1 if( $state & $DI_BUS_DOWN );
456              
457             return %state;
458             }
459              
460             =pod
461              
462             =head2 nodeid
463              
464             Returns the type of the node. Three different strings identifying
465             the types can be returned or C if the type is unknown:
466              
467             PSEUDO
468             SID
469             PROM
470              
471             Nodes of the type C may have additional PROM properties that
472             are defined by the PROM. The properties can be accessed with L.
473              
474             =cut
475              
476             sub nodeid {
477             my $this = shift;
478             my %_nodeid = (
479             $DI_PSEUDO_NODEID => 'PSEUDO',
480             $DI_SID_NODEID => 'SID',
481             $DI_PROM_NODEID => 'PROM',
482             );
483              
484             my $nodeid = di_nodeid( $this->{_data} );
485             my $result = ( exists $_nodeid{ $nodeid } ? $_nodeid{ $nodeid } : undef );
486             return $result;
487             }
488              
489             =pod
490              
491             =head2 is_pseudo_node
492             =head2 is_sid_node
493             =head2 is_prom_node
494              
495             Returns C if the node is of type pseudo / SID / PROM or C if not.
496              
497             =cut
498              
499             sub is_pseudo_node {
500             my $this = shift;
501             return di_nodeid( $this->{_data} ) == $DI_PSEUDO_NODEID ? 'PSEUDO' : undef;
502             }
503              
504             sub is_sid_node {
505             my $this = shift;
506             return di_nodeid( $this->{_data} ) == $DI_SID_NODEID ? 'SID' : undef;
507             }
508              
509              
510             sub is_prom_node {
511             my $this = shift;
512              
513             return di_nodeid( $this->{_data} ) == $DI_PROM_NODEID ? 'PROM' : undef;
514             }
515              
516             =pod
517              
518             =head2 props
519              
520             Returns a reference to a hash which maps property names to property values.
521             The property values are of class L.
522              
523             =cut
524              
525             sub props {
526             my $this = shift;
527             my $node = $this->{_data};
528              
529             if( !exists $this->{_props} ) {
530             my %props;
531             my $prop = di_prop_next( $node, makeDI_PROP_NIL() );
532             while( !isDI_PROP_NIL( $prop ) ) {
533             my $propObj = new Solaris::DeviceTree::Libdevinfo::Property( $prop );
534             $props{ $propObj->name } = $propObj;
535             $prop = di_prop_next( $node, $prop );
536             }
537             $this->{_props} = \%props;
538             }
539             return $this->{_props};
540             }
541              
542             =pod
543              
544             =head2 prom_props
545              
546             Returns a reference to a hash which maps PROM property names to property values.
547             The property values are of class L.
548             If the PROM device can not be opened (most likely because the process does
549             not have the permission to access C) then C is returned.
550              
551             =cut
552              
553             sub prom_props {
554             my $this = shift;
555             my $node = $this->{_data};
556              
557             if( !exists $this->{_prom_props} ) {
558             my %props;
559             my $ph = $this->_prom_handle;
560             if( defined $ph ) {
561             my $handle = newUCharTHandle();
562             my $prop = di_prom_prop_next( $ph, $node, makeDI_PROM_PROP_NIL() );
563             while( !isDI_PROM_PROP_NIL( $prop ) ) {
564             my $name = di_prom_prop_name( $prop );
565             my $count = di_prom_prop_data( $prop, $handle );
566             my $data = pack "C" x $count, map { getIndexedByte( $handle, $_ ) } 0 .. $count-1;
567             $props{ $name } = Solaris::DeviceTree::Libdevinfo::PromProperty->new( $data );
568            
569             $prop = di_prom_prop_next( $ph, $node, $prop );
570             }
571             freeUCharTHandle( $handle );
572             $this->{_prom_props} = \%props;
573             } else {
574             $this->{_prom_props} = undef;
575             }
576             }
577              
578             return $this->{_prom_props};
579             }
580              
581             =pod
582              
583             =head2 minor_nodes
584              
585             Returns a reference to a list of all minor nodes which are associated with this node.
586             The minor nodes are of class L.
587              
588             =cut
589              
590             sub minor_nodes {
591             my $this = shift;
592             my $node = $this->{_data};
593              
594             if( !exists $this->{_minorNodes} ) {
595             my @minorNodes;
596             my $minor = di_minor_next( $node, makeDI_MINOR_NIL() );
597             while( !isDI_MINOR_NIL( $minor ) ) {
598             push @minorNodes, new Solaris::DeviceTree::Libdevinfo::MinorNode( $minor, $this );
599             $minor = di_minor_next( $node, $minor );
600             }
601             $this->{_minorNodes} = \@minorNodes;
602             }
603             return $this->{_minorNodes};
604             }
605              
606             =pod
607              
608             =head1 AUTHOR
609              
610             Copyright 1999-2003 Dagobert Michelsen.
611              
612             =head1 SEE ALSO
613              
614             L, L,
615             L,
616             L,
617             L.
618              
619             =cut
620              
621             1;