]> git.sur5r.net Git - bacula/rescue/blob - rescue/linux/cdrom/yaird-0.0.5/perl/Hardware.pm
adb05519d42a17c6698a42eab1ed3b16e4d99a02
[bacula/rescue] / rescue / linux / cdrom / yaird-0.0.5 / perl / Hardware.pm
1 #!perl -w
2 #
3 # Hardware -- find modules for a thingy in /devices.
4 #   Copyright (C) 2005  Erik van Konijnenburg
5 #
6 #   This program is free software; you can redistribute it and/or modify
7 #   it under the terms of the GNU General Public License as published by
8 #   the Free Software Foundation; either version 2 of the License, or
9 #   (at your option) any later version.
10 #
11 #   This program 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 this program; if not, write to the Free Software
18 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 #
20 #
21 # Method: walk up the path, and for everything you recognise,
22 # prepend the appropriate module.
23 # As an example, for a usb-stick connected via a hub to a pci card,
24 # you'l need usb-storage, a usb protocol driver, and some usb module.
25 #
26 use strict;
27 use warnings;
28 use Conf;
29 use IdeDev;
30 use ScsiDev;
31 use PciDev;
32 use PciTab;
33 use UsbDev;
34 use UsbTab;
35 package Hardware;
36
37
38 sub moduleList ($) {
39         my ($path) = @_;
40         my $result = [];
41         my @components = split (/\/+/, $path);
42         my $devicesPath = Conf::get ('sysDevices');
43         for my $i (0 .. $#components) {
44                 my $abspath = $devicesPath
45                         . "/" . join ('/', @components[0 .. $i]);
46                 my $modules = undef;
47                 if ($abspath =~ m!/pci[0-9a-f]{4}:[0-9a-f]{2}$!) {
48                         # PCI bus; harmless
49                 }
50                 elsif (-f "$abspath/subsystem_vendor") {
51                         # PCI function on a slot.
52                         my $dev = PciDev->new (path => $abspath);
53                         $modules = PciTab::find ($dev);
54                         push @{$modules}, @{addPciSiblings ($abspath)};
55                 }
56
57                 elsif (-f "$abspath/bDeviceClass"
58                         || -f "$abspath/bInterfaceClass")
59                 {
60                         #
61                         # USB.  Every component in the path
62                         # "usb1/1-4/1-4.4/1-4.4:1.0" has either
63                         # a deviceClass or interfaceClass attribute.
64                         #
65                         my $dev = UsbDev->new (path => $abspath);
66                         $modules = UsbTab::find ($dev);
67                 }
68
69                 elsif ($abspath =~ m!/ide\d+$!) {
70                         # IDE bus; harmless
71                 }
72                 elsif ($abspath =~ m!/ide\d+/\d+\.\d+$!) {
73                         # IDE device
74                         my $dev = IdeDev->new (path => $abspath);
75                         $modules = IdeDev::findModuleByIdeDev ($dev);
76                 }
77                 
78                 elsif ($abspath =~ m!/host\d+$!) {
79                         # grouping of SCSI devices; harmless.
80                 }
81                 elsif ($abspath =~ m!/target\d+:\d+:\d+$!) {
82                         # grouping of SCSI devices within a host
83                         # (2.6.10 and later); harmless.
84                 }
85                 elsif ($abspath =~ m!/\d+:\d+:\d+:\d+$!
86                         && -f "$abspath/scsi_level")
87                 {
88                         my $dev = ScsiDev->new (path => $abspath);
89                         $modules = ScsiDev::findModuleByScsiDev ($dev);
90                 }
91
92                 else {
93                         # NOTE: We may want to avoid duplicate messages
94                         Base::warning ("unrecognised device: $abspath");
95                 }
96
97                 if (defined ($modules)) {
98                         push @{$result}, @{$modules};
99                 }
100         }
101         return $result;
102 }
103
104
105 #
106 # addPciSiblings -- probably a bug.
107 #
108 # Here's something odd: my test machine has an USB keyboard, connected
109 # via PCI.  The same 8 ports are visible both as one 8-port EHCI
110 # controller and four 2-port UHCI controllers.  The difference is not
111 # in the hardware, only in the protocol used to talk to the remote device.
112 # These are different PCI functions (0..7) in the same PCI slot (1d):
113 #       0000:00:1d.0 USB Controller: Intel ... USB UHCI #1 (rev 03)
114 #       0000:00:1d.1 USB Controller: Intel ... USB UHCI #2 (rev 03)
115 #       0000:00:1d.2 USB Controller: Intel ... USB UHCI #3 (rev 03)
116 #       0000:00:1d.3 USB Controller: Intel ... USB UHCI #4 (rev 03)
117 #       0000:00:1d.7 USB Controller: Intel ... USB2 EHCI Controller (rev 03)
118 #
119 # The keyboard shows up under the EHCI controller, a printer shows up
120 # under one of the UHCI controllers.
121 # If we load only the EHCI module, the UHCI-only printer causes some
122 # complaints, and the keyboard is not detected (unless you try to
123 # debug this via a serial line ...)
124 # If you load UHCI as well, the keyboard is detected flawlessly.
125 #
126 # We could interpret this as a bug in EHCI, and claim that a non-EHCI
127 # device on one of the ports should not interfere with detecting devices
128 # on other ports, but it's more productive to see this as an example of
129 # how some PCI devices work better if there's a driver for every
130 # function in the slot.
131 #
132 # (Or you could just add a special case to always add an UHCI driver
133 # after EHCI, but then you would have to consider OHCI as well, plus
134 # think about blacklisting and competing USB driver implementations.
135 # I'd rather not go there.)
136 #
137 # The kernel function pci_setup_device() uses the following format
138 # for PCI function directories in sysfs:
139 #       "%04x:%02x:%02x.%d", domain, bus, slot, function.
140 #
141 # Given an absolute path to a PCI function directory, return a list
142 # of modules needed for all USB functions *except* the one specified by
143 # the path, but only if the path refers to a USB function.
144 #
145 # Limiting this behaviour to USB functions alone is desirable, given
146 # the existence of chips such as VT8366/A/7, that combine ISA, IDE,
147 # USB and multimedia in a single PCI slot.
148 #
149 sub addPciSiblings ($) {
150         my ($abspath) = @_;
151         my $modules = [];
152         if (! isUsb ($abspath)) {
153                 return $modules;
154         }
155
156         my $dirName = Base::dirname ($abspath);
157         my $cur = Base::basename ($abspath);
158
159         if ($cur !~ /^([0-9a-f]{4}):([0-9a-f]{2}):([0-9a-f]{2})\.(\d)$/) {
160                 Base::fatal ("Odd PCI directory in sysfs: $abspath");
161         }
162         my $domain = $1;
163         my $bus = $2;
164         my $slot = $3;
165         my $function = $4;
166
167         #print "D $dirName, B $cur, d $domain b $bus s $slot f $function\n";
168         my $dir;
169         if (! opendir ($dir, $dirName)) {
170                 Base::fatal ("can't open directory $dirName");
171         }
172         while (defined(my $entry = readdir($dir))) {
173                 if ($entry !~ /^([0-9a-f]{4}):([0-9a-f]{2}):([0-9a-f]{2})\.(\d+)$/) {
174                         next;
175                 }
176                 if (! ($1 eq $domain && $2 eq $bus && $3 eq $slot
177                         && $4 ne $function))
178                 {
179                         next;
180                 }
181
182                 # OK, it's in my slot, and it isn't me.
183                 # Add required modules if it's USB.
184                 my $sibling = "$dirName/$entry";
185                 if (isUsb ($sibling)) {
186                         my $dev = PciDev->new (path => $sibling);
187                         push @{$modules}, @{PciTab::find ($dev)};
188                 }
189         }
190         if (! closedir ($dir)) {
191                 Base::fatal ("could not read directory $dirName");
192         }
193         return $modules;
194 }
195
196 #
197 # isUsb -- given an absolute path into sysfs, true iff it controls a usb port.
198 #
199 sub isUsb ($) {
200         my ($abspath) = @_;
201         my $dir;
202         my $result = 0;
203         if (! opendir ($dir, $abspath)) {
204                 Base::fatal ("can't open directory $abspath");
205         }
206         while (defined(my $entry = readdir($dir))) {
207                 if ($entry =~ /^usb\d+$/) {
208                         $result = 1;
209                 }
210         }
211         if (! closedir ($dir)) {
212                 Base::fatal ("could not read directory $abspath");
213         }
214         return $result;
215 }
216
217 1;
218