diff options
Diffstat (limited to 'perl-install/detect_devices.pm')
| -rw-r--r-- | perl-install/detect_devices.pm | 1669 | 
1 files changed, 1356 insertions, 313 deletions
| diff --git a/perl-install/detect_devices.pm b/perl-install/detect_devices.pm index 57dd842f1..48dadadbb 100644 --- a/perl-install/detect_devices.pm +++ b/perl-install/detect_devices.pm @@ -1,4 +1,4 @@ -package detect_devices; # $Id$ +package detect_devices;  use diagnostics;  use strict; @@ -8,22 +8,63 @@ use vars qw($pcitable_addons $usbtable_addons);  #- misc imports  #-######################################################################################  use log; +use MDK::Common; # help perl_checker  use common;  use devices;  use run_program; +use modules;  use c; +use feature 'state'; + + +=head1 SYNOPSYS + +The B<detect_devices> modules offers a high level API for detecting devices.* +It mostly relies on the L<c> modules for gluing  libldetect back into the Perl world, and thus +being able to enumerate DMI/HID/PCI/USB devices. + +Other devices are mostly detected through C</proc> & C</sys>. + +Then the L<list_modules> enables to map modules into categories such as: + +=over 4 + +=item * C<network/ethernet,> + +=item * C<network/wireless,> + +=item * C<network/wifi,> + +=item * C<disk/sata,> + +=item * C<disk/scsi,> + +=item * ... + +=back + +This enables to detect a category by mapping drivers to categories. + +=head1 Listing block devices + +=cut  #-#####################################################################################  #- Globals  #-##################################################################################### -my @netdevices = map { my $l = $_; map { "$l$_" } (0..3) } qw(eth tr fddi plip);  my %serialprobe;  #-######################################################################################  #- Functions  #-###################################################################################### -sub dev_is_devfs() { -e "/dev/.devfsd" } #- no $::prefix, returns false during install and that's nice :) +=over + +=item get() + +Returns a list of all block devices (IDE, SCSI, SATA, virtual, RAID, ...) + +=cut  sub get() {      #- Detect the default BIOS boot harddrive is kind of tricky. We may have IDE, @@ -33,70 +74,51 @@ sub get() {      #- 2. The first SCSI device if SCSI exists. Or      #- 3. The first RAID device if RAID exists. -    getIDE(), getSCSI(), getDAC960(), getCompaqSmartArray(), getATARAID(); +    getIDE(), getSCSI(), getMmcBlk(), getNVMe(), getXenBlk(), getVirtIO(), getDAC960(), getCompaqSmartArray();  } -sub hds()         { grep { $_->{media_type} eq 'hd' && !isRemovableDrive($_) } get() } +sub hds()         { grep { may_be_a_hd($_) } get() }  sub tapes()       { grep { $_->{media_type} eq 'tape' } get() }  sub cdroms()      { grep { $_->{media_type} eq 'cdrom' } get() }  sub burners()     { grep { isBurner($_) } cdroms() }  sub dvdroms()     { grep { isDvdDrive($_) } cdroms() }  sub raw_zips()    { grep { member($_->{media_type}, 'fd', 'hd') && isZipDrive($_) } get() } -#-sub jazzs     { grep { member($_->{media_type}, 'fd', 'hd') && isJazzDrive($_) } get() }  sub ls120s()      { grep { member($_->{media_type}, 'fd', 'hd') && isLS120Drive($_) } get() }  sub zips()        {      map {   	$_->{device} .= 4;  -	$_->{devfs_device} = $_->{devfs_prefix} . '/part4';   	$_;      } raw_zips();  } -sub cdroms__faking_ide_scsi() { grep { $_->{media_type} eq 'cdrom' } cdroms_and_zips__faking_ide_scsi() } -sub cdroms_and_zips__faking_ide_scsi() { -    my @l = grep { $_->{media_type} eq 'cdrom' || member($_->{media_type}, 'fd', 'hd') && isZipDrive($_) } get(); - -    if (my @l_need_fake = grep { !$::isStandalone && $_->{bus} eq 'ide' && !($_->{media_type} eq 'cdrom' && !isBurner($_)) } @l) { -	require modules; -	modules::add_probeall('scsi_hostadapter', 'ide-scsi'); - -	my $nb_cdrom = 1 + max(-1, map { $_->{device} =~ /scd(\d+)/ } @l); -	my $nb_zip = 1 + max(-1, map { if_($_->{device} =~ /sd(\w+)/, ord($1) - ord('a')) } getSCSI()); -	my $scsi_hostadapters = modules::get_probeall('scsi_hostadapter'); -	my $devfs_host = find_index { $_ eq 'ide-scsi' } @$scsi_hostadapters; -	my $devfs_id = 0; - -	foreach my $e (@l_need_fake) { -	    $e->{devfs_prefix} = sprintf('scsi/host%d/bus0/target%d/lun0', $devfs_host, $devfs_id++); -	    my $faked; -	    if ($e->{media_type} eq 'cdrom') { -		$faked = "scd" . $nb_cdrom++; -		log::l("IDEBurner: $e->{device} => $faked and $e->{devfs_prefix}"); -	    } else { -		$faked = "sd" . chr(ord('a') + $nb_zip++); -		log::l("IDE Zip: $e->{device} => $faked and $e->{devfs_prefix}"); -	    } -	    $e->{device} = $faked; -	} -	get_devfs_devices(@l_need_fake); -    } -    foreach (@l) { -	$_->{device} .= 4 if $_->{media_type} ne 'cdrom'; -	$_->{devfs_device} = $_->{devfs_prefix} . '/' . ($_->{media_type} eq 'cdrom' ? 'cd' : 'part4'); -    } -    @l; -} - -sub floppies() { +sub floppies { +    my ($o_not_detect_legacy_floppies) = @_;      require modules; -    eval { modules::load("floppy") }; -    my @fds = $@ ? () : map { -	my $info = (!dev_is_devfs() || -e "/dev/fd$_") && c::floppy_info(devices::make("fd$_")); -	if_($info && $info ne '(null)', { device => "fd$_", devfs_device => "floppy/$_", media_type => 'fd', info => $info }) -    } qw(0 1); - -    my @ide = ls120s() and eval { modules::load("ide-floppy") }; +    state @fds; +    state $legacy_already_detected; + +    $o_not_detect_legacy_floppies = 1 if arch() =~ /arm/; + +    if (!$o_not_detect_legacy_floppies && !$legacy_already_detected) { +        $legacy_already_detected = 1; +        if ($::isInstall) { +            eval { modules::load("floppy") }; +            system(qw(udevadm settle)) if !$@; +        } +        #- do not bother probing /dev/fd0 and loading floppy device uselessly, +        #- it takes time and it is already done by boot process (if not in install): +        #-   /dev/fd0 is created by start_udev (/etc/udev/devices.d/default.nodes) +        #-   then hal probes /dev/fd0 and triggers floppy module loading through kernel's kmod +        if (any { (split)[1] eq 'fd' } cat_("/proc/devices")) { +            @fds = map { +                my $info = c::floppy_info("/dev/fd$_"); +                if_($info && $info ne '(null)', { device => "fd$_", media_type => 'fd', info => $info }); +            } qw(0 1); +        } +    } +         +    my @ide = ls120s() and eval { modules::load("ide_floppy") }; -    eval { modules::load("usb-storage") } if usbStorage(); +    eval { modules::load("usb_storage") } if $::isInstall && usbStorage();      my @scsi = grep { $_->{media_type} eq 'fd' } getSCSI();      @ide, @scsi, @fds;  } @@ -105,7 +127,7 @@ sub floppy() { first(floppies_dev()) }  #- example ls120, model = "LS-120 SLIM 02 UHD Floppy"  sub removables() { -    floppies(), cdroms_and_zips__faking_ide_scsi(); +    floppies(), cdroms(), zips();  }  sub get_sys_cdrom_info { @@ -118,7 +140,6 @@ sub get_sys_cdrom_info {  	@l = split(' ', $l) if $l;  	if ($t eq 'drive name') {  	    @drives_order = map { -		s/^sr/scd/;  		my $dev = $_;  		find { $_->{device} eq $dev } @drives;  	    } @l; @@ -138,52 +159,27 @@ sub get_sys_cdrom_info {      }  } -sub get_usb_storage_info { +sub complete_usb_storage_info {      my (@l) = @_; -    my %usbs = map { -	my $s = cat_(glob_("$_/*")); -	my ($host) = $s =~ /^\s*Host scsi(\d+):/m; #-# -	my ($vendor_name) = $s =~ /^\s*Vendor: (.*)/m; -	my ($vendor, $id) = $s =~ /^\s*GUID: (....)(....)/m; -	if_(defined $host, $host => { vendor_name => $vendor_name, usb_vendor => hex $vendor, usb_id => hex $id }); -    } glob_('/proc/scsi/usb-storage-*') or return; - -    #- only the entries matching the following conditions can be usb-storage devices -    @l = grep { $_->{channel} == 0 && $_->{id} == 0 && $_->{lun} == 0 } @l; -    my %l; push @{$l{$_->{host}}}, $_ foreach @l; - -    my @informed; -    foreach my $host (keys %usbs) { -	my @choices = @{$l{$host} || []} or log::l("weird, host$host from /proc/scsi/usb-storage-*/* is not in /proc/scsi/scsi"), next; -	if (@choices > 1) { -	    @choices = grep { $_->{info} =~ /^\Q$usbs{$host}{vendor_name}/ } @choices; -	    @choices or log::l("weird, can't find the good entry host$host from /proc/scsi/usb-storage-*/* in /proc/scsi/scsi"), next; -	    @choices == 1 or log::l("argh, can't determine the good entry host$host from /proc/scsi/usb-storage-*/* in /proc/scsi/scsi"), next -	} -	add2hash($choices[0], $usbs{$host}); -	push @informed, $choices[0]; -    } -    @informed or return; +    my @usb = grep { exists $_->{usb_vendor} } @l;      foreach my $usb (usb_probe()) { -	if (my $e = find { $_->{usb_vendor} == $usb->{vendor} && $_->{usb_id} == $usb->{id} } @informed) { -	    $e->{"usb_$_"} = $usb->{$_} foreach keys %$usb; +	if (my $e = find { !$_->{found} && $_->{usb_vendor} == $usb->{vendor} && $_->{usb_id} == $usb->{id} } @usb) { +         my $host = get_sysfs_usbpath_for_block($e->{device}); +         if ($host) { +             my $file = "/sys/block/$host/../serial"; +             $e->{info} = chomp_(cat_($file)) if -e $file; +             $e->{usb_description} = join('|',  +                                          chomp_(cat_("/sys/block/$host/../manufacturer")), +                                          chomp_(cat_("/sys/block/$host/../product"))); +         } +         local $e->{found} = 1; +	    $e->{"usb_$_"} ||= $usb->{$_} foreach keys %$usb;  	}      }  } -sub get_devfs_devices { -    my (@l) = @_; - -    my %h = (cdrom => 'cd', hd => 'disc'); - -    foreach (@l) { -	my $t = $h{$_->{media_type}} or next; -	$_->{devfs_device} = $_->{devfs_prefix} . '/' . $t; -    } -} -  sub isBurner {       my ($e) = @_;      $e->{capacity} =~ /burner/ and return 1; @@ -201,66 +197,137 @@ sub isDvdDrive {      $f && c::isDvdDrive(fileno($f));  }  sub isZipDrive { $_[0]{info} =~ /ZIP\s+\d+/ } #- accept ZIP 100, untested for bigger ZIP drive. -sub isJazzDrive { $_[0]{info} =~ /\bJAZZ?\b/i } #- accept "iomega jaz 1GB"  sub isLS120Drive { $_[0]{info} =~ /LS-?120|144MB/ } -sub isRemovableUsb { $_[0]{usb_media_type} && index($_[0]{usb_media_type}, 'Mass Storage|') == 0 && usb2removable($_[0]) } +sub isKeyUsb { begins_with($_[0]{usb_media_type} || '', 'Mass Storage') && $_[0]{media_type} eq 'hd' }  sub isFloppyUsb { $_[0]{usb_driver} && $_[0]{usb_driver} eq 'Removable:floppy' } -sub isRemovableDrive {  +sub may_be_a_hd {       my ($e) = @_; -    isZipDrive($e) || isLS120Drive($e) || $e->{media_type} && $e->{media_type} eq 'fd' || isRemovableUsb($e) || $e->{usb_media_type} && index($e->{usb_media_type}, 'Mass Storage|Floppy (UFI)') == 0; +    $e->{media_type} eq 'hd' && !( +	isZipDrive($e)  +           || isLS120Drive($e) +           || begins_with($e->{usb_media_type} || '', 'Mass Storage|Floppy (UFI)') +    ); +} + +sub get_sysfs_field_from_link { +    my ($device, $field) = @_; +    my $l = readlink("$device/$field"); +    $l =~ s!.*/!!; +    $l; +} + +sub get_sysfs_usbpath_for_block { +    my ($device) = @_; +    my $host = readlink("/sys/block/$device"); +    $host =~ s!/host.*!!; +    $host; +} + +sub get_scsi_driver { +    my (@l) = @_; +    # find driver of host controller from sysfs: +    foreach (@l) { +	next if $_->{driver}; +	my $host = get_sysfs_usbpath_for_block($_->{device}); +	require list_modules; +	$_->{driver} = list_modules::filename2modname(get_sysfs_field_from_link("/sys/block/$host", 'driver')); +    }  } +=item getSCSI() + +Returns a list of all SCSI device. + +=cut +  sub getSCSI() { -    my $err = sub { log::l("ERROR: unexpected line in /proc/scsi/scsi: $_[0]") }; - -    my ($first, @l) = common::join_lines(cat_("/proc/scsi/scsi")) or return; -    $first =~ /^Attached devices:/ or $err->($first); - -    @l = map_index { -	my ($host, $channel, $id, $lun) = m/^Host: scsi(\d+) Channel: (\d+) Id: (\d+) Lun: (\d+)/ or $err->($_); -	my ($vendor, $model) = /^\s*Vendor:\s*(.*?)\s+Model:\s*(.*?)\s+Rev:/m or $err->($_); -	my ($type) = /^\s*Type:\s*(.*)/m or $err->($_); -	{ info => "$vendor $model", host => $host, channel => $channel, id => $id, lun => $lun,  -	  device => "sg$::i", devfs_prefix => sprintf('scsi/host%d/bus%d/target%d/lun%d', $host, $channel, $id, $lun), -          raw_type => $type, bus => 'SCSI' }; -    } @l; - -    get_usb_storage_info(@l); - -    each_index { -	my $dev = "sd" . chr($::i + ord('a')); -	put_in_hash $_, { device => $dev, media_type => isFloppyUsb($_) ? 'fd' : 'hd' }; -    } grep { $_->{raw_type} =~ /Direct-Access|Optical Device/ } @l; - -    each_index { -	put_in_hash $_, { device => "st$::i", media_type => 'tape' }; -    } grep { $_->{raw_type} =~ /Sequential-Access/ } @l; - -    each_index { -	put_in_hash $_, { device => "scd$::i", media_type => 'cdrom' }; -    } grep { $_->{raw_type} =~ /CD-ROM|WORM/ } @l; - -    # Old hp scanners report themselves as "Processor"s -    # (see linux/include/scsi/scsi.h and sans-find-scanner.1) -    each_index { -	put_in_hash $_, { media_type => 'scanner' }; -    } grep { $_->{raw_type} =~ /Scanner/ || $_->{raw_type} =~ /Processor / } @l; - -    get_devfs_devices(@l); +    my $dev_dir = '/sys/bus/scsi/devices'; + +    my @scsi_types = ( +	"Direct-Access", +	"Sequential-Access", +	"Printer", +	"Processor", +	"WORM", +	"CD-ROM", +	"Scanner", +	"Optical Device", +	"Medium Changer", +	"Communications", +    ); + +    my @l; +    foreach (all($dev_dir)) { +	my ($host, $channel, $id, $lun) = split ':'; +	defined $lun or next; + +	my $dir = "$dev_dir/$_"; + +	# handle both old and new kernels: +	my $node =  find { -e $_ } "$dir/block", top(glob_("$dir/block*")), "$dir/tape", top(glob_("$dir/scsi_generic*")); +	my ($device) = readlink($node) =~ m!/(?:scsi_(?:generic|tape)|block)/(.*)!; +        if (!$device) { +            ($device) = top(glob_("$node/*")) =~ m!/(?:scsi_(?:generic|tape)|block)/(.*)!; +        } +	warn("cannot get info for device ($_)"), next if !$device; + +	my $usb_dir = readlink($dir) =~ m!/usb! && "$dir/../../../.."; +	my $get_usb = sub { chomp_(cat_("$usb_dir/$_[0]")) }; + +	my $get = sub { +	    my $s = cat_("$dir/$_[0]"); +	    $s =~ s/\s+$//; +	    $s; +	}; + +	# Old HP scanners report themselves as "Processor"s +	# (see linux/include/scsi/scsi.h and sans-find-scanner.1) +	my $raw_type = $scsi_types[$get->('type')]; + +	my $media_type = ${{ st => 'tape', sr => 'cdrom', sd => 'hd', sg => 'generic' }}{substr($device, 0, 2)} || +	  $raw_type =~ /Scanner|Processor/ && 'scanner'; + +	my ($vendor, $model) = ($get->('vendor'), $get->('model')); +	my ($v, $m) = _get_hd_vendor($model); +	if ($v && $m) { +            ($vendor, $model) = ($v, $m); +	} +	push @l, { info =>  $vendor . ' ' . $model, host => $host, channel => $channel, id => $id, lun => $lun,  +	  description => join('|', $vendor, $model), +	  bus => 'SCSI', media_type => $media_type, device => $device, +	    $usb_dir ? ( +	  usb_vendor => hex($get_usb->('idVendor')), usb_id => hex($get_usb->('idProduct')), +	    ) : (), +        }; +    }  + +    @l = sort { $a->{host} <=> $b->{host} || $a->{channel} <=> $b->{channel} || $a->{id} <=> $b->{id} || $a->{lun} <=> $b->{lun} } @l; + +    complete_usb_storage_info(@l); + +    foreach (@l) { +	$_->{media_type} = 'fd' if $_->{media_type} eq 'hd' && isFloppyUsb($_); +    } +      get_sys_cdrom_info(@l); +    get_scsi_driver(@l);      @l;  } -my %eide_hds = ( + +my %hd_vendors = (      "ASUS" => "Asus", +    "ATA Maxtor" => "Maxtor",      "CD-ROM CDU" => "Sony",      "CD-ROM Drive/F5D" => "ASUSTeK",      "Compaq" => "Compaq",      "CONNER" => "Conner Peripherals",      "IBM" => "IBM", +    "INTEL" => "Intel",      "FUJITSU" => "Fujitsu",      "HITACHI" => "Hitachi",      "Lite-On" => "Lite-On Technology Corp.", +    "LITE-ON" => "Lite-On Technology Corp.",      "LTN" => "Lite-On Technology Corp.",      "IOMEGA" => "Iomega",      "MAXTOR" => "Maxtor", @@ -277,6 +344,21 @@ my %eide_hds = (      "WDC" => "Western Digital Corp.",  ); +# return ($vendor, $model) +sub _get_hd_vendor { +    my ($info) = @_; +    foreach my $name (keys %hd_vendors) { +        next if !$name; +        return ($hd_vendors{$name}, $2) if $info =~ /^$name(-|\s)*(.*)/; +    } +    return ("Hitachi", $info) if $info =~ /^HD[ST][0-9]/; +} + +=item getIDE() + +Returns a list of all IDE device. + +=cut  sub getIDE() {      my @idi; @@ -286,47 +368,61 @@ sub getIDE() {      #- Great. 2.2 kernel, things are much easier and less error prone.      foreach my $d (sort @{[glob_('/proc/ide/hd*')]}) { -	cat_("$d/driver") =~ /ide-scsi/ and next; #- already appears in /proc/scsi/scsi  	my $t = chomp_(cat_("$d/media"));  	my $type = ${{ disk => 'hd', cdrom => 'cdrom', tape => 'tape', floppy => 'fd' }}{$t} or next;  	my $info = chomp_(cat_("$d/model")) || "(none)";  	my $num = ord(($d =~ /(.)$/)[0]) - ord 'a'; -	my ($vendor, $model) = map {  -	    if_($info =~ /^$_(-|\s)*(.*)/, $eide_hds{$_}, $2); -	} keys %eide_hds; +	my ($vendor, $model) = _get_hd_vendor($info); -	my ($channel, $id) = ($num / 2, $num % 2); -	my $devfs_prefix = sprintf('ide/host0/bus%d/target%d/lun0', $channel, $id); +	my $host = $num; +	($host, my $id) = divide($host, 2); +	($host, my $channel) = divide($host, 2);  	push @idi, { media_type => $type, device => basename($d),  -		     devfs_prefix => $devfs_prefix, -		     info => $info, channel => $channel, id => $id, bus => 'ide',  +		     info => $info, host => $host, channel => $channel, id => $id, bus => 'ide',   		     if_($vendor, Vendor => $vendor), if_($model, Model => $model) };      } -    get_devfs_devices(@idi);      get_sys_cdrom_info(@idi);      @idi;  } +sub block_devices() { +    -d '/sys/block'  +      ? map { s|!|/|; $_ } all('/sys/block')  +      : map { $_->{dev} } do { require fs::proc_partitions; fs::proc_partitions::read_raw() }; +} + +=item getCompaqSmartArray() + +Returns a list of all CCISS devices (Compaq Smart Array). + +=cut +  sub getCompaqSmartArray() {      my (@idi, $f);      foreach ('array/ida', 'cpqarray/ida', 'cciss/cciss') { -	my $prefix = "/proc/driver/$_"; #- kernel 2.4 places it here -	$prefix = "/proc/$_" if !-e "${prefix}0"; #- kernel 2.2 - +	my $prefix = "/proc/driver/$_";  	my ($name) = m|/(.*)|;  	for (my $i = 0; -r ($f = "${prefix}$i"); $i++) { -	    foreach (cat_($f)) { -		if (my ($device) = m|^\s*($name/.*?):|) { -		    push @idi, { device => $device, prefix => $device . 'p', info => "Compaq RAID logical disk", -				 media_type => 'hd', bus => 'ida' }; -		} +	    my @raw_devices = cat_($f) =~ m|^\s*($name/.*?):|gm; + +	    #- this is ugly and buggy. keeping it for 2007.0 +	    #- on a cciss, cciss/cciss0 didn't contain c0d0, but cciss/cciss1 did contain c0d1 +	    #- the line below adds both c0d0 and c0d1 for cciss0, and so some duplicates +	    @raw_devices or @raw_devices = grep { m!^$name/! } block_devices(); + +	    foreach my $raw_device (@raw_devices) { +		my $device = -d "/dev/$raw_device" ? "$raw_device/disc" : $raw_device; +		push @idi, { device => $device, prefix => $raw_device . 'p',  +			     info => "Compaq RAID logical disk", +			     media_type => 'hd', bus => $name };  	    }  	}      } -    @idi; +    #- workaround the buggy code above. this should be safe though +    uniq_ { $_->{device} } @idi;  }  sub getDAC960() { @@ -336,30 +432,60 @@ sub getDAC960() {      #- /dev/rd/c0d0: RAID-7, Online, 17928192 blocks, Write Thru0123456790123456789012      foreach (syslog()) {  	my ($device, $info) = m|/dev/(rd/.*?): (.*?),| or next; -	$idi{$device} = { info => $info, media_type => 'hd', device => $device, prefix => $device . 'p', bus => 'dac960' }; +	$idi{$device} = { info => $info, media_type => 'hd', device => $device, bus => 'dac960' };      }      values %idi;  } -sub getATARAID() { -    my %l; -    foreach (syslog()) { -	my ($device) = m|^\s*(ataraid/d\d+):| or next; -	$l{$device} = { info => 'ATARAID block device', media_type => 'hd', device => $device, prefix => $device . 'p', bus => 'ataraid' }; -	log::l("ATARAID: $device"); +=item getXenBlk() + +Returns a list of all Xen block devices (C</dev/xvd*>). + +=cut + +sub getXenBlk() { +    -d '/sys/bus/xen/devices' or return; +    map {    +            s/block://; +            { device => basename($_), info => "Xen block device", media_type => 'hd', bus => 'xen' }; +    } glob("/sys/block/xvd*"); +} + +=item getVirtIO() + +Returns a list of all VirtIO block devices (/dev/C<vd*>). + +=cut + +sub getVirtIO() { +    -d '/sys/bus/virtio/devices' or return; +    map { +            { device => basename($_), info => "VirtIO block device", media_type => 'hd', bus => 'virtio' };      } -    values %l; +    glob("/sys/bus/virtio/devices/*/block/*");  } +sub getMmcBlk() { +    -d '/sys/bus/mmc/devices' or return; +    map { +            { device => basename($_), info => "MMC block device", media_type => 'hd', bus => 'mmc' }; +    } +    glob("/sys/bus/mmc/devices/*/block/*"); +} -# cpu_name : arch() =~ /^alpha/ ? "cpu	" : -# arch() =~ /^ppc/ ? "processor" : "vendor_id" +sub getNVMe() { +    -d '/sys/class/nvme' or return; +    map { +            { device => basename($_), info => "NVMe block device", media_type => 'hd', bus => 'pci_express' }; +    } +    glob("/sys/block/nvme*"); +} + +=item getCPUs() -# cpu_model : arch() =~ /^alpha/ ? "cpu model" : -# arch() =~ /^ppc/ ? "cpu  " : "model name" +Returns a list of all CPUs. -# cpu_freq = arch() =~ /^alpha/ ? "cycle frequency [Hz]" : -# arch() =~ /^ppc/ ? "clock" : "cpu MHz" +=cut  sub getCPUs() {       my (@cpus, $cpu); @@ -369,61 +495,472 @@ sub getCPUs() {  		  $cpu = {};  	   }  	   $cpu->{$1} = $2 if /^([^\t]+).*:\s(.*)$/; +	   $cpu->{processor}++ if $1 eq "processor";      }      push @cpus, $cpu;      @cpus;  } +sub ix86_cpu_frequency() { +    cat_('/proc/cpuinfo') =~ /cpu MHz\s*:\s*(\d+)/ && $1; +} + +=item probe_category($category) + +Returns a list of devices which drivers are in the asked category. eg: + +   my @eth_cards = probe_category('network/ethernet'); + +=cut + +sub probe_category { +    my ($category) = @_; + +    require list_modules; +    my @modules = list_modules::category2modules($category); + +    grep { +	if ($category eq 'network/isdn') { +	    my $b = $_->{driver} =~ /ISDN:([^,]*),?([^,]*)(?:,firmware=(.*))?/; +	    if ($b) { +                $_->{driver} = $1; +                $_->{type} = $2; +                $_->{type} =~ s/type=//; +                $_->{firmware} = $3; +                $_->{driver} eq "hisax" and $_->{options} .= " id=HiSax"; +	    } +	    $b; +	} else { +	    member($_->{driver}, @modules); +	} +    } probeall(); +} +  sub getSoundDevices() { -    (arch() =~ /ppc/ ? \&modules::load_category : \&modules::probe_category)->('multimedia/sound'); +    probe_category('multimedia/sound');  } -sub isTVcard { $_[0]{driver} =~ /bttv|saa7134/ } +sub isTVcardConfigurable { member($_[0]{driver}, qw(bttv cx88 saa7134)) } + +sub getTVcards() { probe_category('multimedia/tv') } + +sub getInputDevices() { +    state @input_devices; +    return @input_devices if @input_devices; +    my (@devices, $device); +    my $event; +    foreach (cat_('/proc/bus/input/devices')) { +        if (/^I:/) { +            $device = {}; +            $device->{vendor} = /Vendor=(\w+)/ && $1; +            $device->{id} = /Product=(\w+)/ && $1; +            $device->{bustype} = /Bus=(\w+)/ && $1; +        } elsif (/N: Name="(.*)"/) { +	    my $descr = $1; +	    $device->{description} = "|$descr"; + +	    #- I: Bus=0011 Vendor=0002 Product=0008 Version=7321 +	    #- N: Name="AlpsPS/2 ALPS GlidePoint" +	    #- P: Phys=isa0060/serio1/input0 +	    #- H: Handlers=mouse1 event2 ts1 +	    #- B: EV=f +	    #- B: KEY=420 0 70000 0 0 0 0 0 0 0 0 #=> BTN_LEFT BTN_RIGHT BTN_MIDDLE BTN_TOOL_FINGER BTN_TOUCH +	    #-    or B: KEY=420 0 670000 0 0 0 0 0 0 0 0 #=> same with BTN_BACK +	    #- B: REL=3       #=> X Y +	    #- B: ABS=1000003 #=> X Y PRESSURE + +	    #- I: Bus=0011 Vendor=0002 Product=0008 Version=2222 +	    #- N: Name="AlpsPS/2 ALPS DualPoint TouchPad" +	    #- P: Phys=isa0060/serio1/input0 +	    #- S: Sysfs=/class/input/input2 +	    #- H: Handlers=mouse1 ts1 event2  +	    #- B: EV=f +	    #- B: KEY=420 0 70000 0 0 0 0 0 0 0 0 +	    #- B: REL=3 +	    #- B: ABS=1000003 + +	    #- I: Bus=0011 Vendor=0002 Product=0007 Version=0000 +	    #- N: Name="SynPS/2 Synaptics TouchPad" +	    #- P: Phys=isa0060/serio1/input0 +	    #- S: Sysfs=/class/input/input1 +	    #- H: Handlers=mouse0 event1 ts0 +	    #- B: EV=b +	    #- B: KEY=6420 0 70000 0 0 0 0 0 0 0 0 #=> BTN_LEFT BTN_RIGHT BTN_MIDDLE BTN_TOOL_FINGER BTN_TOUCH BTN_TOOL_DOUBLETAP BTN_TOOL_TRIPLETAP +	    #-    or B: KEY=6420 0 670000 0 0 0 0 0 0 0 0  #=> same with BTN_BACK +	    #-    or B: KEY=420 30000 670000 0 0 0 0 0 0 0 0 #=> same without BTN_TOOL_TRIPLETAP but with BTN_B +	    #- B: ABS=11000003 #=> X Y PRESSURE TOOL_WIDTH + +	    #- I: Bus=0003 Vendor=056a Product=0065 Version=0108 +	    #- N: Name="Wacom Bamboo" +	    #- B: KEY=1c63 0 70033 0 0 0 0 0 0 0 0 #=> BTN_0 BTN_1 BTN_4 BTN_5 BTN_LEFT BTN_RIGHT BTN_MIDDLE TOOL_PEN TOOL_RUBBER TOOL_BRUSH TOOL_FINGER TOOL_MOUSE TOUCH STYLUS STYLUS2 +            #- B: ABS=100 3000103 #=> X Y WHEEL PRESSURE DISTANCE MISC + +	    #- I: Bus=0003 Vendor=056a Product=0011 Version=0201 +	    #- N: Name="Wacom Graphire2 4x5" +            #- B: KEY=1c43 0 70000 0 0 0 0 0 0 0 0 #=> BTN_LEFT BTN_RIGHT BTN_MIDDLE TOOL_PEN TOOL_RUBBER TOOL_FINGER TOOL_MOUSE TOUCH STYLUS STYLUS2 +            #- B: ABS=100 3000003 #=> X Y PRESSURE DISTANCE MISC + +            #I: Bus=0003 Vendor=049f Product=0024 Version=0001 +	    #N: Name="Compaq Compaq Numeric Keypad" +	    #P: Phys=usb-0000:00:03.2-2/input0 +	    #S: Sysfs=/class/input/input7 +	    #H: Handlers=kbd event3 +	    #B: EV=120003 +	    #B: KEY=10000 7 ff800000 7ff febeffdf ffefffff ffffffff fffffffe +	    #B: LED=1f + +	    $device->{Synaptics} = $descr eq 'SynPS/2 Synaptics TouchPad'; +	    $device->{ALPS} = $descr =~ m!^AlpsPS/2 ALPS!; +	    $device->{Elantech} = $descr eq 'ETPS/2 Elantech Touchpad'; +	    $device->{Numpad} = $descr =~ /Numeric Keypad/; + +	} elsif (/H: Handlers=(.*)/) { +	    my @l = split(' ', $1); +	    $device->{driver} = $l[0]; #- keep it for compatibility +	    $device->{Handlers} = +{ map { (/^(.*?)\d*$/ ? $1 : $_, $_) } split(' ', $1) }; +	    $event = $device->{Handlers}{event}; +	} elsif (/S: Sysfs=(.+)/) { +	    $device->{sysfs_path} = $1; +	} elsif (/P: Phys=(.*)/) { +            $device->{location} = $1; +            $device->{bus} = 'isa' if $device->{location} =~ /^isa/; +            $device->{bus} = 'usb' if $device->{location} =~ /^usb/i; +	} elsif (/B: REL=(.* )?(.*)/) { +	    #- REL=3   #=> X Y +	    #- REL=103 #=> X Y WHEEL +	    #- REL=143 #=> X Y HWHEEL WHEEL +	    #- REL=1c3 #=> X Y HWHEEL DIAL WHEEL +	    my $REL = hex($2); +	    $device->{HWHEEL} = 1 if $REL & (1 << 6); +	    $device->{WHEEL} = 1 if $REL & (1 << 8); #- not reliable ("Mitsumi Apple USB Mouse" says REL=103 and KEY=1f0000 ...) + +	} elsif (/B: KEY=(\S+)/) {	    +	    #- some KEY explained: +	    #- (but note that BTN_MIDDLE can be reported even if missing) +	    #- (and "Mitsumi Apple USB Mouse" reports 1f0000) +	    #- KEY=30000 0 0 0 0 0 0 0 0  #=> BTN_LEFT BTN_RIGHT +	    #- KEY=70000 0 0 0 0 0 0 0 0  #=> BTN_LEFT BTN_RIGHT BTN_MIDDLE +	    #- KEY=1f0000 0 0 0 0 0 0 0 0 #=> BTN_LEFT BTN_RIGHT BTN_MIDDLE BTN_SIDE BTN_EXTRA +	    if (!$> && ! -f "/dev/input/$event") { +		    devices::make("/dev/input/$event"); +	    } +	    if (-r "/dev/input/$event") { +		my @KEYS = c::EVIocGBitKey("/dev/input/$event"); +		$device->{SIDE} = 1 if $KEYS[0] & (1 << 0x13); +	    } else { +		my $KEY = hex($1); +		$device->{SIDE} = 1 if $KEY & (1 << 0x13); +	    } -sub getTVcards() {  -    grep { isTVcard($_) } detect_devices::probeall(); +        } elsif (/^\s*$/) { +	    push @devices, $device if $device; +	    undef $device; +	} +    } +    @input_devices = @devices;  } +sub getInputDevices_and_usb() { +    my @l = getInputDevices(); + +    foreach my $usb (usb_probe()) { +	if (my $e = find { hex($_->{vendor}) == $usb->{vendor} && hex($_->{id}) == $usb->{id} } @l) { +	    $e->{usb} = $usb; +	} +    } + +    @l; +} + +sub serialPorts() { map { "ttyS$_" } 0..7 } +sub serialPort2text { +    $_[0] =~ /ttyS(\d+)/ ? "$_[0] / COM" . ($1 + 1) : $_[0]; +} + +=back + +=head1 Network + +=over + +=cut +  sub getSerialModem { -    my ($modem, $o_mouse) = @_; +    my ($_modules_conf, $o_mouse) = @_;      my $mouse = $o_mouse || {};      $mouse->{device} = readlink "/dev/mouse"; -    my $serdev = arch() =~ /ppc/ ? "macserial" : "serial"; +    my $serdev = "serial"; +      eval { modules::load($serdev) }; +    my @modems; +      probeSerialDevices(); -    foreach ('modem', map { "ttyS$_" } (0..7)) { -	next if $mouse->{device} =~ /$_/; -	next unless -e "/dev/$_"; -	hasModem("/dev/$_") and $modem->{device} = $_, last; +    foreach my $port (serialPorts()) { +	next if $mouse->{device} =~ /$port/; +     my $device = "/dev/$port"; +	next if !-e $device || !hasModem($device); +     $serialprobe{$device}{device} = $device; +     push @modems, $serialprobe{$device};      } - -    #- add an alias for macserial on PPC -    modules::add_alias('serial', $serdev) if arch() =~ /ppc/ && $modem->{device};      my @devs = pcmcia_probe(); -    foreach (@devs) { $_->{type} =~ /serial/ and $modem->{device} = $_->{device} } -    $modem; +    foreach my $modem (@modems) { +        foreach (@devs) { $_->{device} and $modem->{device} = $_->{device} } +    } +    @modems; +} + +=item getModem() + +Return list of modems (serial, WinModems) +=cut + +our $detect_serial_modem = 1; +sub getModem { +    my ($modules_conf) = @_; +    ($detect_serial_modem ? getSerialModem($modules_conf, {}) : ()), get_winmodems(); +} + +sub get_winmodems() { +    matching_driver__regexp('www\.linmodems\.org'), +    matching_driver(list_modules::category2modules('network/modem'), +    list_modules::category2modules('network/slmodem')); +} + +sub getBewan() { +    matching_desc__regexp('Bewan Systems\|.*ADSL|BEWAN ADSL USB|\[Unicorn\]'); +} + +# generate from the following from eci driver sources: +# perl -e 'while (<>) { print qq("$1$2",\n"$3$4",\n) if /\b([a-z\d]*)\s*([a-z\d]*)\s*([a-z\d]*)\s*([a-z\d]*)$/ }' <modems.db|sort|uniq +sub getECI() { +    my @ids = ( +              "05090801", +              "05472131", +              "06590915", +              "071dac81", +              "08ea00c9", +              "09150001", +              "09150002", +              "091500ca", +              "091500e7", +              "09150101", +              "09150102", +              "09150204", +              "09150206", +              "09150802", +              "09150916", +              "09158000", +              "09158001", +              "0915ac82", +              "0baf00e6", +              "0e600100", +              "0e600101", +              "0fe88000", +              "16900203", +              "16900205", +             ); +    grep { member(sprintf("%04x%04x%04x%04x", $_->{vendor}, $_->{id}, $_->{subvendor}, $_->{subid}), @ids) } usb_probe(); +} + +=item get_xdsl_usb_devices() + +Return list of xDSL devices. + +=cut + +sub get_xdsl_usb_devices() { +    my @bewan = detect_devices::getBewan(); +    $_->{driver} = $_->{bus} eq 'USB' ? 'unicorn_usb_atm' : 'unicorn_pci_atm' foreach @bewan; +    my @eci = detect_devices::getECI(); +    $_->{driver} = 'eciusb' foreach @eci; +    my @usb = detect_devices::probe_category('network/usb_dsl'); +    $_->{description} = "USB ADSL modem (eagle chipset)" foreach +      grep { $_->{driver} eq 'ueagle_atm' && $_->{description} eq '(null)' } @usb; +    @usb, @bewan, @eci; +} + +=item is_lan_interface($device) + +Is it a LAN interface (blacklist some interfaces (PPP, ...)? + +=cut + +sub is_lan_interface { +    #- we want LAN like interfaces here (eg: ath|br|eth|fddi|plip|ra|tr|usb|wlan). +    #- there's also bnep%d for bluetooth, bcp%d... +    #- we do this by blacklisting the following interfaces: +    #-   hso%d are created by drivers/net/usb/hso.c +    #-   ippp|isdn|plip|ppp (initscripts suggest that isdn%d can be created but kernel sources claim not) +    #-   ippp%d are created by drivers/isdn/i4l/isdn_ppp.c +    #-   plip%d are created by drivers/net/plip.c +    #-   ppp%d are created by drivers/net/ppp_generic.c +    #-   pan%d are created by bnep +    is_useful_interface($_[0]) && +    $_[0] !~ /^(?:hso|ippp|isdn|plip|ppp|pan)/; +} + +=item is_useful_interface($device) + +Is it a usefull interface (blacklist some interfaces (loopback, sit, wifi, ...)? + +=cut + +sub is_useful_interface { +    #- sit0 which is *always* created by net/ipv6/sit.c, thus is always created since net.agent loads ipv6 module +    #- wifi%d are created by 3rdparty/hostap/hostap_hw.c (pseudo statistics devices, #14523) +    #- wmaster%d are created by net/mac80211/ieee80211.c ("master" 802.11 device) +    #- ax*, rose*, nr*, bce* and scc* are Hamradio devices (#28776) +    $_[0] !~ /^(?:lo|sit0|wifi|wmaster|ax|rose|nr|bce|scc)/;  } -sub getModem() { -    my @pci_modems = grep { $_->{driver} =~ /www.linmodems.org/ } probeall(0); -    getSerialModem({}), @pci_modems; +=item is_wireless_interface($device) + +Is it a WiFi interface? + +=cut + +sub is_wireless_interface { +    my ($interface) = @_; +    #- some wireless drivers don't always support the SIOCGIWNAME ioctl +    #-   ralink devices need to be up to support it +    #-   wlan-ng (prism2_*) need some special tweaks to support it +    #- use sysfs as fallback to detect wireless interfaces, +    #- i.e interfaces for which get_wireless_stats() is available +    c::isNetDeviceWirelessAware($interface) +        || -e "/sys/class/net/$interface/wireless" +        || -e "/sys/class/net/$interface/phy80211"; +} + +=item get_all_net_devices() + +Returns list of all network devices + +=cut + +sub get_all_net_devices() { +    #- we need both detection schemes since: +    #-   get_netdevices() use the SIOCGIFCONF ioctl that does not list interfaces that are down +    #-   /proc/net/dev does not list VLAN and IP aliased interfaces +    uniq( +        (map { if_(/^\s*([A-Za-z0-9:\.]*):/, $1) } cat_("/proc/net/dev")), +        c::get_netdevices(), +    ); +} + +=item get_lan_interfaces() + +Returns list of all LAN devices + +=cut + +sub get_lan_interfaces() { grep { is_lan_interface($_) } get_all_net_devices() } + +=item get_net_interfaces() + +Returns list of all useful network devices + +=cut + +sub get_net_interfaces() { grep { is_useful_interface($_) } get_all_net_devices() } + +=item get_wireless_interface() + +Returns list of all Wireless devices + +=cut + +sub get_wireless_interface() { find { is_wireless_interface($_) } get_lan_interfaces() } + +=item is_bridge_interface($interface) + +Is it a bridge? + +=cut + +sub is_bridge_interface { +    my ($interface) = @_; +    -f "/sys/class/net/$interface/bridge/bridge_id";  } -sub getSpeedtouch() { -    grep { $_->{description} eq 'Alcatel|USB ADSL Modem (Speed Touch)' } probeall(0); +=back + +=head1 Enumerating devices + +=over + +=cut + +sub get_ids_from_sysfs_device { +    my ($dev_path) = @_; +    my $dev_cat = sub { chomp_(cat_("$dev_path/$_[0]")) }; +    my $usb_root = -f "$dev_path/bInterfaceNumber" && "../" || -f "$dev_path/idVendor" && ""; +    my $is_pcmcia = -f "$dev_path/card_id"; +    my $sysfs_ids; +    my $bus = get_sysfs_field_from_link($dev_path, "subsystem"); +    #- FIXME: use $bus +    if ($is_pcmcia) { +      $sysfs_ids = { modalias => $dev_cat->('modalias') }; +    } else { +        $sysfs_ids = $bus eq 'ieee1394' ? +          { +            version => "../vendor_id", +            specifier_id => "specifier_id", +            specifier_version => "version", +          } : +        defined $usb_root ? +          { id => $usb_root . 'idProduct', vendor => $usb_root . 'idVendor' } : +          { id => "device", subid => "subsystem_device", vendor => "vendor", subvendor => "subsystem_vendor" }; +        $_ = hex($dev_cat->($_)) foreach values %$sysfs_ids; +        if ($bus eq 'pci') { +            my $device = basename(readlink $dev_path); +            my @ids = $device =~ /^(.{4}):(.{2}):(.{2})\.(.+)$/; +            @$sysfs_ids{qw(pci_domain pci_bus pci_device pci_function)} = map { hex($_) } @ids if @ids; +        } +    } +    $sysfs_ids;  } -sub getSagem() { -    grep { $_->{description} eq 'Analog Devices Inc.|USB ADSL modem' } probeall(0); + +sub device_matches_sysfs_ids { +    my ($device, $sysfs_ids) = @_; +    every { defined $device->{$_} && member($device->{$_}, $sysfs_ids->{$_}, 0xffff) } keys %$sysfs_ids;  } -sub getNet() { -    grep { !(($::isStandalone || $::live) && /plip/) && c::hasNetDevice($_) } @netdevices; +sub device_matches_sysfs_device { +  my ($device, $dev_path) = @_; +  device_matches_sysfs_ids($device, get_ids_from_sysfs_device($dev_path));  } -#sub getISDN() { -#    mapgrep(sub {member (($_[0] =~ /\s*(\w*):/), @netdevices), $1 }, split(/\n/, cat_("/proc/net/dev"))); -#} +sub getUPS() { +    # MGE serial PnP devices: +    (map { +        $_->{port} = $_->{DEVICE}; +        $_->{bus} = "Serial"; +        $_->{driver} = "mge-utalk" if $_->{MODEL} =~ /0001/; +        $_->{driver} = "mge-shut"  if $_->{MODEL} =~ /0002/; +        $_->{media_type} = 'UPS'; +        $_->{description} = "MGE UPS SYSTEMS|UPS - Uninterruptible Power Supply" if $_->{MODEL} =~ /000[12]/; +        $_; +    } grep { $_->{DESCRIPTION} =~ /MGE UPS/ } values %serialprobe), +    # USB UPSs; +    (map { ($_->{name} = $_->{description}) =~ s/.*\|//; $_ } +        map { +            if ($_->{description} =~ /Back-UPS/ && $_->{driver} eq 'usbhid') { +                #- FIXME: should not be hardcoded, use $_->{sysfs_device} . */usb:(hiddev\d+) +                #- the device should also be assigned to the ups user +                $_->{port} = "/dev/hiddev0"; +                $_->{driver} = 'hidups'; +                $_; +            } elsif ($_->{description} =~ /^MGE UPS Systems\|/ && $_->{driver} =~ /ups$/) { +                $_->{port} = "auto"; +                $_->{media_type} = 'UPS'; +                $_->{driver} = 'newhidups'; +                $_; +            } else { +                (); +            } +        } usb_probe()); +}  $pcitable_addons = <<'EOF';  # add here lines conforming the pcitable format (0xXXXX\t0xXXXX\t"\w+"\t".*") @@ -452,11 +989,11 @@ sub add_addons {      my ($addons, @l) = @_;      foreach (split "\n", $addons) { -	/^\s/ and die "bad detect_devices::probeall_addons line \"$_\""; +	/^\s/ and die qq(bad detect_devices::probeall_addons line "$_");  	s/^#.*//;  	s/"(.*?)"/$1/g;  	next if /^$/; -	my ($vendor, $id, $driver, $description) = split("\t", $_, 4) or die "bad detect_devices::probeall_addons line \"$_\""; +	my ($vendor, $id, $driver, $description) = split("\t", $_, 4) or die qq(bad detect_devices::probeall_addons line "$_");  	foreach (@l) {  	    $_->{vendor} == hex $vendor && $_->{id} == hex $id or next;  	    put_in_hash($_, { driver => $driver, description => $description }); @@ -465,128 +1002,645 @@ sub add_addons {      @l;  } -sub pci_probe { -    my ($probe_type) = @_; -    log::l("full pci_probe") if $probe_type; +sub get_pci_sysfs_path { +    my ($l) = @_; +    sprintf('%04x:%02x:%02x.%d', $l->{pci_domain}, $l->{pci_bus}, $l->{pci_device}, $l->{pci_function}); +} + + +my (@pci, @usb); + +sub pci_probe__real() {      add_addons($pcitable_addons, map { -	my %l; -	@l{qw(vendor id subvendor subid pci_bus pci_device pci_function media_type driver description)} = split "\t"; -	$l{$_} = hex $l{$_} foreach qw(vendor id subvendor subid); -	$l{bus} = 'PCI'; -	\%l -    } c::pci_probe($probe_type || 0)); +	my $l = $_; +	$l->{bus} = 'PCI'; +	$l->{sysfs_device} = '/sys/bus/pci/devices/' . get_pci_sysfs_path($l); +	$l; +    } c::pci_probe());  } -sub usb_probe() { -    -e "/proc/bus/usb/devices" or return; +=item pci_probe() + +Cache the result of C<c::pci_probe()> and return the list of items in the PCI devices. + +=cut + +sub pci_probe() { +    state $done; +    if (!$done) { +        @pci = pci_probe__real() if !@pci; +        foreach (@pci) { +            $_->{nice_bus} = $_->{is_pciexpress} ? "PCI Express" : "PCI"; +        } +    } +    @pci; +} + +sub usb_probe__real() { +    -e "/sys/kernel/debug/usb/devices" or return;      add_addons($usbtable_addons, map { -	my %l; -	@l{qw(vendor id media_type driver description pci_bus pci_device)} = split "\t"; -	$l{$_} = hex $l{$_} foreach qw(vendor id); -	$l{bus} = 'USB'; -	\%l +	my $l = $_; +	$l->{media_type} = join('|', grep { $_ ne '(null)' } split('\|', $l->{media_type})); +	$l->{sysfs_device} = "/sys/bus/usb/devices/$l->{pci_bus}-" . ($l->{usb_port} + 1); +	$l->{bus} = 'USB'; +	$l;      } c::usb_probe());  } -sub pcmcia_probe() { -    -e '/var/run/stab' || -e '/var/lib/pcmcia/stab' or return (); +=item usb_probe() -    my (@devs, $desc); -    foreach (cat_('/var/run/stab'), cat_('/var/lib/pcmcia/stab')) { -	if (/^Socket\s+\d+:\s+(.*)/) { -	    $desc = $1; -	} else { -	    my (undef, $type, $module, undef, $device) = split; -	    push @devs, { description => $desc, driver => $module, type => $type, device => $device }; +Cache the result of C<c::usb_probe()> and return the list of items in the USB devices. + +=cut + +sub usb_probe() { +    if ($::isStandalone && @usb) { +	    @usb; +    } else { +	    @usb = usb_probe__real(); +    } +} + +=item firewire_probe() + +Return list of Firewire controllers + +=cut + +sub firewire_probe() { +    my $dev_dir = '/sys/bus/ieee1394/devices'; +    my @l = map { +        my $dir = "$dev_dir/$_"; +        my $get = sub { chomp_(cat_($_[0])) }; +        { +            version => hex($get->("$dir/../vendor_id")), +            specifier_id => hex($get->("$dir/specifier_id")), +            specifier_version => hex($get->("$dir/version")), +            bus => 'Firewire', +            sysfs_device => $dir, +        }; +    } grep { -f "$dev_dir/$_/specifier_id" } all($dev_dir); + +    my $e; +    foreach (cat_('/proc/bus/ieee1394/devices')) { +	if (m!Vendor/Model ID: (.*) \[(\w+)\] / (.*) \[(\w+)\]!) { +	    push @l, $e = {  +			   vendor => hex($2), id => hex($4),  +			   description => join('|', $1, $3), +			   bus => 'Firewire', +			  }; +	} elsif (/Software Specifier ID: (\w+)/) { +	    $e->{specifier_id} = hex $1; +	} elsif (/Software Version: (\w+)/) { +	    $e->{specifier_version} = hex $1;	      	}      } -    @devs; + +    foreach (@l) { +	if ($_->{specifier_id} == 0x00609e && $_->{specifier_version} == 0x010483) { +	    add2hash($_, { driver => 'sbp2', description => "Generic Firewire Storage Controller" }); +	} elsif ($_->{specifier_id} == 0x00005e && $_->{specifier_version} == 0x000001) { +	    add2hash($_, { driver => 'eth1394', description => "IEEE 1394 IPv4 Driver (IPv4-over-1394 as per RFC 2734)" }); +	} +    } +    @l;  } -# pci_probe with $probe_type is unsafe for pci! (bug in kernel&hardware) -# pcmcia_probe provides field "device", used in network.pm -# => probeall with $probe_type is unsafe -sub probeall { -    my ($probe_type) = @_; +=item pcmcia_controller_probe() + +Return list of PCMCIA controllers + +=cut + +sub pcmcia_controller_probe() { +    my ($controller) =  probe_category('bus/pcmcia'); +    if (!$controller && !$::testing && !$::noauto && arch() =~ /i.86/) { +        my $driver = c::pcmcia_probe(); +        $controller = { driver => $driver, description => "PCMCIA controller ($driver)" } if $driver; +    } +    $controller; +} + +=item pcmcia_probe() + +Return list of PCMCIA devices (eg: Ethernet PCMCIA cards, ...) + +=cut + +sub pcmcia_probe() { +    require modalias; +    require modules; +    my $dev_dir = '/sys/bus/pcmcia/devices'; +    map { +        my $dir = "$dev_dir/$_"; +        my $get = sub { chomp_(cat_("$dir/$_[0]")) }; +        my $class_dev = first(glob_("$dir/tty/tty*")); +        my $device = $class_dev && basename($class_dev); +        my $modalias = $get->('modalias'); +        my $driver = get_sysfs_field_from_link($dir, 'driver'); +        #- fallback on modalias result +        #- but only if the module isn't loaded yet (else, it would already be binded) +        #- this prevents from guessing the wrong driver for multi-function devices +        my $module = $modalias && first(modalias::get_modules($modalias)); +        $driver ||= !member($module, modules::loaded_modules()) && $module; +        { +            description => join(' ', grep { $_ } map { $get->("prod_id$_") } 1 .. 4), +            driver => $driver, +            if_($modalias, modalias => $modalias), +            if_($device, device => $device), +            bus => 'PCMCIA', +            sysfs_device => $dir, +        }; +    } all($dev_dir); +} + +=item dmi_probe() + +Cache the result of c::dmi_probe() (aka C<dmidecode>) and return the list of items in the DMI table + +=cut + +sub dmi_probe() { +    state $dmi_probe; +    return if arch() !~ /86/; +    $dmi_probe ||= $> ? [] : [ c::dmi_probe() ]; +    @$dmi_probe; +} + +=item acpi_probe() + +Return list of devices that are only discoverable via ACPI + +=cut +my %acpi_device_info = ( +    "80860F28" => { vendor => 0x8086, id => 0x0f28, description => 'Intel Corporation|Bay Trail SST Audio DSP', +                    nice_media_type => 'Audio device', driver => 'snd_sof_acpi_intel_byt' }, +    "808622A8" => { vendor => 0x8086, id => 0x22a8, description => 'Intel Corporation|Cherry Trail SST Audio DSP', +                    nice_media_type => 'Audio device', driver => 'snd_sof_acpi_intel_byt' }, +    "INT3438"  => { vendor => 0x8086, id => 0x3438, description => 'Intel Corporation|Broadwell SST Audio DSP', +                    nice_media_type => 'Audio device', driver => 'snd_sof_acpi_intel_bdw' } +); + +sub acpi_probe() { +    my $dev_dir = '/sys/bus/acpi/devices'; +    my @l; +    foreach (glob("$dev_dir/*")) { +        my $dev_name = basename($_); +        my ($prefix, $suffix) = split(':', $dev_name); +        my $e = $acpi_device_info{$prefix}; +        if ($e) { +            add2hash($e, { bus => 'ACPI', sysfs_device => "$dev_dir/$dev_name" }); +            push @l, $e; +        } +    } +    @l; +} + +=item probeall() + +Returns a list of all PCI/USB/Firewire/PCMCIA/DMI/HID devices. +It's usually called through a higher level filtering function. + +=cut + +# pcmcia_probe provides field "device", used in network.pm +sub probeall() {      return if $::noauto; -    require sbus_probing::main; -    pci_probe($probe_type), usb_probe(), pcmcia_probe(), sbus_probing::main::probe(); +    pci_probe(), usb_probe(), firewire_probe(), pcmcia_probe(), dmi_probe(), acpi_probe(), getInputDevices_and_usb();  } -sub matching_desc { +sub probeall_update_cache() { +    return if $::noauto; +    @pci = pci_probe__real(), @usb = usb_probe__real(), firewire_probe(), pcmcia_probe(), dmi_probe(); +} +sub matching_desc__regexp {      my ($regexp) = @_;      grep { $_->{description} =~ /$regexp/i } probeall();  } -sub stringlist {  +sub matching_driver__regexp { +    my ($regexp) = @_; +    grep { $_->{driver} =~ /$regexp/i } probeall(); +} + +sub matching_driver { +    my (@list) = @_; +    grep { member($_->{driver}, @list) } probeall(); +} +sub probe_name { +    my ($name) = @_; +    map { $_->{driver} =~ /^$name:(.*)/ } probeall(); +} +sub probe_unique_name { +    my ($name) = @_; +    my @l = uniq(probe_name($name)); +    if (@l > 1) { +	log::l("oops, more than one $name from probe: ", join(' ', @l)); +    } +    $l[0]; +} + +sub stringlist { +    my ($b_verbose) = @_;      map { +	my $ids = $b_verbose || $_->{description} eq '(null)' ?  sprintf("vendor:%04x device:%04x", $_->{vendor}, $_->{id}) : ''; +	my $subids = $_->{subid} && $_->{subid} != 0xffff ? sprintf("subv:%04x subd:%04x", $_->{subvendor}, $_->{subid}) : '';  	sprintf("%-16s: %s%s%s",   		$_->{driver} || 'unknown',  -		$_->{description} eq '(null)' ? sprintf("Vendor=0x%04x Device=0x%04x", $_->{vendor}, $_->{id}) : $_->{description}, +		$_->{description},  		$_->{media_type} ? sprintf(" [%s]", $_->{media_type}) : '', -		$_->{subid} && $_->{subid} != 0xffff ? sprintf(" SubVendor=0x%04x SubDevice=0x%04x", $_->{subvendor}, $_->{subid}) : '', -	       ) -    } probeall(@_);  +		$ids || $subids ? " ($ids" . ($ids && $subids && " ") . "$subids)" : '', +	       ); +    } probeall();   }  sub tryOpen($) {      my $F; -    sysopen $F, devices::make($_[0]), c::O_NONBLOCK() and $F; +    sysopen($F, devices::make($_[0]), c::O_NONBLOCK()) && $F;  }  sub tryWrite($) {      my $F; -    sysopen $F, devices::make($_[0]), 1 | c::O_NONBLOCK() and $F; +    sysopen($F, devices::make($_[0]), 1 | c::O_NONBLOCK()) && $F;  } +my @dmesg;  sub syslog() { -    -r "/tmp/syslog" and return map { /<\d+>(.*)/ } cat_("/tmp/syslog"); -    my $LD_LOADER = $ENV{LD_LOADER} || ""; -    `$LD_LOADER /bin/dmesg`; +    if (-r "/tmp/syslog") { +	map { /<\d+>(.*)/ } cat_("/tmp/syslog"); +    } else { +	@dmesg = `/bin/dmesg` if !@dmesg; +	@dmesg; +    }  }  sub get_mac_model() { -    my $mac_model = cat_("/proc/device-tree/model") || die "Can't open /proc/device-tree/model"; +    my $mac_model = cat_("/proc/device-tree/model") || die "Cannot open /proc/device-tree/model";      log::l("Mac model: $mac_model");      $mac_model;	  }  sub get_mac_generation() { -    my $generation = cat_("/proc/cpuinfo") || die "Can't open /proc/cpuinfo"; -    my @genarray = split(/\n/, $generation); -    my $count = 0; -    while ($count <= @genarray) { -	if ($genarray[$count] =~ /pmac-generation/) { -	    @genarray = split(/:/, $genarray[$count]); -	    return $genarray[1]; +    cat_('/proc/cpuinfo') =~ /^pmac-generation\s*:\s*(.*)/m ? $1 : "Unknown Generation";	 +} + +sub hasSMP() {  +    return if $::testing; +    (any { /NR_CPUS limit of 1 reached/ } syslog()) || +     (any { /^processor\s*:\s*(\d+)/ && $1 > 0 } cat_('/proc/cpuinfo')) || +      any { /\bProcessor #(\d+)\s+(\S*)/ && $1 > 0 && $2 ne 'invalid' } syslog(); +} +sub hasPCMCIA() { $::o->{pcmcia} } + +my (@dmis, $dmidecode_already_runned); + +=item dmidecode()  + +Return list of DMI categories from DMI table + +=cut +# we return a list b/c several DMIs have the same name: +sub dmidecode() { +    return @dmis if $dmidecode_already_runned; + +    return if $>; +    my ($_ver, @l) = arch() =~ /86/ ? run_program::get_stdout('dmidecode') : (); + +    #- drop header +    shift @l while @l && $l[0] ne "\n"; + +    foreach (@l) { +	next if /TRUNCATED/; +	if (/^\t(.*)/) { +	    $dmis[-1]{string} .= "$1\n"; +	    $dmis[-1]{$1} = $2 if /^\t(.*): (.*)$/; +	} elsif (my ($s) = /^(.*)/) { +	    next if $s =~ /^$/ || $s =~ /\bDMI type \d+/; +	    $s =~ s/ Information$//; +	    push @dmis, { name => $s };  	} -	$count++;      } -    return "Unknown Generation";	 +    $dmidecode_already_runned = 1; +    @dmis; +} + +=item dmi_detect_memory($category) + +Return only one category from DMI table + +=cut + +sub dmidecode_category { +    my ($cat) = @_; +    my @l = grep { $_->{name} eq $cat } dmidecode(); +    wantarray() ? @l : $l[0] || {}; +} + +=item dmi_detect_memory() + +Return RAM size in MB according to DMI table + +=cut + +sub dmi_detect_memory() { +    my @l1 = map { $_->{'Enabled Size'} =~ /(\d+) MB/ && $1 } dmidecode_category('Memory Module'); +    my @l2 = map { $_->{'Form Factor'} =~ /^(SIMM|SIP|DIP|DIMM|FB-DIMM|RIMM|SODIMM|SRIMM)$/ && 		      +		     ($_->{Size} =~ /(\d+) MB/ && $1 || $_->{Size} =~ /(\d+) kB/ && $1 * 1024); +		 } dmidecode_category('Memory Device'); +    max(sum(@l1), sum(@l2)); +} + +=back + +=head1 Test helpers + +=over + +=item computer_info() + +Analyse "Chassis" & "Bios" in dmidecode output and return a hash of flags/values (isLaptop, isServer, BIOS_Year) + +=cut + +sub computer_info() { +     my $Chassis = dmidecode_category('Chassis')->{Type} =~ /(\S+)/ && $1; + +     my $date = dmidecode_category('BIOS')->{'Release Date'} || ''; +     my $BIOS_Year = $date =~ m!(\d{4})! && $1 || +	             $date =~ m!\d\d/\d\d/(\d\d)! && "20$1"; +	 +     +{  +	 isLaptop => member($Chassis, 'Portable', 'Laptop', 'Notebook', 'Hand Held', 'Sub Notebook', 'Docking Station'), +	 isServer => member($Chassis, 'Pizza Box', 'Main Server Chassis', 'Rack Mount Chassis', 'Blade'), +	 if_($BIOS_Year, BIOS_Year => $BIOS_Year), +     };  } -sub hasSMP() { !$::testing && c::detectSMP() } -sub hasPCMCIA() { $::o->{pcmcia} } #- because /proc/pcmcia seems not to be present on 2.4 at least (or use /var/run/stab) +=item isLaptop() + +try to detect a laptop. We assume the following is an indication of a laptop: + +=over 4 + +=item * + +pcmcia service + +=item * + +C<computer_info()> (really C<dmidecode>) telling us it's a laptop + +=item * + +ACPI lid button + +=item * + +a regexp to match graphics card apparently only used for such systems. + +=item * + +Mobility CPU + +=item * + +having Type as Laptop in some device + +=item * + +Intel ipw2100/2200/3945 Wireless + +=back + +=cut -#- try to detect a laptop, we assume pcmcia service is an indication of a laptop or -#- the following regexp to match graphics card apparently only used for such systems.  sub isLaptop() { -    hasPCMCIA() || (matching_desc('C&T.*655[45]\d') || matching_desc('C&T.*68554') || -		    matching_desc('Neomagic.*Magic(Media|Graph)') || -		    matching_desc('ViRGE.MX') || matching_desc('S3.*Savage.*[IM]X') || -		    matching_desc('ATI.*(Mobility|LT)')); +      computer_info()->{isLaptop} +	|| glob_("/sys/bus/acpi/devices/PNP0C0D:*") #- ACPI lid button +	|| (matching_desc__regexp('C&T.*655[45]\d') || matching_desc__regexp('C&T.*68554') || +	    matching_desc__regexp('Neomagic.*Magic(Media|Graph)') || +	    matching_desc__regexp('ViRGE.MX') || matching_desc__regexp('S3.*Savage.*[IM]X') || +	    matching_desc__regexp('Intel Corporation\|Mobile') || +	    matching_desc__regexp('\bATI\b.*(Mobility|\bLT\b)')) +	|| (any { $_->{'model name'} =~ /\b(mobile|C7-M)\b/i } getCPUs()) +	|| probe_unique_name("Type") eq 'laptop' +        #- ipw2100/2200/3945 are Mini-PCI (Express) adapters +	|| (any { member($_->{driver}, qw(ipw2100 ipw2200 ipw3945)) } pci_probe()); +} + +=item isServer() + +Is it a server? + +=cut + +sub isServer() { +    computer_info()->{isServer} +      || (any { $_->{Type} =~ /ECC/ } dmidecode_category('Memory Module')) +      || dmidecode_category('System Information')->{Manufacturer} =~ /Supermicro/i +      || dmidecode_category('System Information')->{'Product Name'} =~ /NetServer|Proliant|PowerEdge|eServer|IBM System x|ThinkServer/i +      || matching_desc__regexp('LSI Logic.*SCSI') +      || matching_desc__regexp('MegaRAID') +      || matching_desc__regexp('NetServer') +      || (any { $_->{'model name'} =~ /(Xeon|Opteron)/i } getCPUs()); +} + +sub BIGMEM() { +    arch() !~ /x86_64/ && $> == 0 && dmi_detect_memory() > 4 * 1024; +} + +sub is_arm_openrd_client() { +    to_bool(cat_('/proc/cpuinfo') =~ /OpenRD Client/); +} + +sub is_arm_versatile() { +    to_bool(cat_('/proc/cpuinfo') =~ /ARM-Versatile/); +} + +sub is_arm_efikamix() { +    to_bool(cat_('/proc/cpuinfo') =~ /Efika MX/); +} + +sub is_xbox() { +    any { $_->{vendor} == 0x10de && $_->{id} == 0x02a5 } pci_probe(); +} + +=item virt_technology() + +Returns the virtualization technology (eg: kvm, oracle, ...) + +=cut + +sub virt_technology() { +    state $tech; +    $tech ||= chomp_(run_program::get_stdout('systemd-detect-virt')); +} + +=item is_hyperv() + +Are we running under Hyper-V hypervisor? + +=cut + +sub is_hyperv() { +    virt_technology() eq 'microsoft'; +} + +=item is_qemu() + +Are we running under Qemu hypervisor? + +=cut + +sub is_qemu() { +    member(virt_technology(), qw(kvm qemu)); +} + +=item is_virtualbox() + +Are we running under VirtualBox hypervisor? + +=cut + +sub is_virtualbox() { +    virt_technology() eq 'oracle'; +} + +=item is_vmware() + +Are we running under VMware hypervisor? + +=cut + +sub is_vmware() { +    virt_technology() eq 'vmware'; +} + +=item is_netbook_nettop() + +Is it a NetBook? + +=cut + +sub is_netbook_nettop() { +    my @cpus = getCPUs(); +    (any { $_->{'model name'} =~ /(\bIntel\(R\) Atom\(TM\)\B)/i } @cpus) || +    (any { $_->{'model name'} =~ /(\bIntel\(R\) Celeron\(R\) M processor\b|\bVIA C7-M Processor\b|\bGeode\(TM\)\B)/i && $_->{'cpu MHz'} < 1500 } @cpus); +} + +=item has_low_resources() + +Is it a low resource machine? + +=cut + +sub has_low_resources() { +    availableRamMB() < 100 || arch() =~ /i.86/ && ix86_cpu_frequency() < 350; +} + +=item need_light_desktop() + +Does it need a light desktop (netbook or low resources machine)? + +=cut + +sub need_light_desktop() { +    has_low_resources() || is_netbook_nettop(); +} + +=item has_cpu_flag($flag) + +Does CPU has this flag + +=cut + +sub has_cpu_flag { +    my ($flag) = @_; +    cat_('/proc/cpuinfo') =~ /^flags.*\b$flag\b/m;  } -sub usbMice()      { grep { $_->{media_type} =~ /\|Mouse/ && $_->{driver} !~ /Tablet:wacom/ || -			  $_->{driver} =~ /Mouse:USB/ } usb_probe() } -sub usbWacom()     { grep { $_->{driver} =~ /Tablet:wacom/ } usb_probe() } +=item sub matching_types() + +Returns a hash of flags (laptop, hyperv, touchpad, 64bit, wireless, ...) + +=cut + +sub matching_types() { +    +{ +	'64bit' => to_bool(arch() =~ /64/), +	hyperv => is_hyperv(), +	laptop => isLaptop(), +	numpad => hasNumpad(), +	touchpad => hasTouchpad(), +	qemu => is_qemu(), +	virtualbox => is_virtualbox(), +	vmware => is_vmware(), +	wireless => to_bool(get_wireless_interface() || probe_category('network/wireless')), +    }; +} + +=item sub hasCPUMicrocode() + +Does CPU need microcode updates? + +=cut + +sub hasCPUMicrocode() { +    state $hasCPUMicrocode; +    if (!defined $hasCPUMicrocode) { +        $hasCPUMicrocode = to_bool(find { 'microcode' } modules::loaded_modules()); +    } +    return $hasCPUMicrocode; +} + +=item sub hasCPUFreq() + +Does CPU support cpufreq? + +=cut + +sub hasCPUFreq()     { +    require cpufreq; +     to_bool(cpufreq::get_modules()) +       || cat_('/proc/cpuinfo') =~ /AuthenticAMD/ && arch() =~ /x86_64/ +       || cat_('/proc/cpuinfo') =~ /model name.*Intel\(R\) Core\(TM\)2 CPU/; +} + +=item sub hasWacom() + +is there a Wacom tablet? + +=cut + +sub hasWacom()     { find { $_->{vendor} == 0x056a || $_->{driver} =~ /wacom/ } usb_probe() } + +=item sub hasTouchpad() + +is there a touchpad? + +=cut + +sub hasTouchpad()  { any { $_->{Synaptics} || $_->{ALPS} || $_->{Elantech} } getInputDevices() } + +=item sub hasNumpad() + +is there a numeric pad? + +=cut + +sub hasNumpad()    { any { $_->{Numpad} } getInputDevices() } + +sub usbWacom()     { grep { $_->{vendor} eq '056a' } getInputDevices() }  sub usbKeyboards() { grep { $_->{media_type} =~ /\|Keyboard/ } usb_probe() }  sub usbStorage()   { grep { $_->{media_type} =~ /Mass Storage\|/ } usb_probe() } +sub has_mesh()     { find { /mesh/ } all_files_rec("/proc/device-tree") } +sub has_53c94()    { find { /53c94/ } all_files_rec("/proc/device-tree") }  sub usbKeyboard2country_code {      my ($usb_kbd) = @_;      my ($F, $tmp); +    # FIXME: usbfs is dead! we should poke somewhere in /sys/ !!!!      sysopen($F, sprintf("/proc/bus/usb/%03d/%03d", $usb_kbd->{pci_bus}, $usb_kbd->{pci_device}), 0) and        sysseek $F, 0x28, 0 and        sysread $F, $tmp, 1 and @@ -594,6 +1648,9 @@ sub usbKeyboard2country_code {  }  sub probeSerialDevices() { +    require list_modules; +    require modules; +    modules::append_to_modules_loaded_at_startup_for_all_kernels(modules::load_category($::o->{modules_conf}, 'various/serial'));      foreach (0..3) {  	#- make sure the device are created before probing,  	devices::make("/dev/ttyS$_"); @@ -611,9 +1668,14 @@ sub probeSerialDevices() {      print STDERR "Please wait while probing serial ports...\n";      #- start probing all serial ports... really faster than before ...      #- ... but still take some time :-) -    my %current; foreach (run_program::get_stdout('serial_probe')) { -	$serialprobe{$current{DEVICE}} = { %current } and %current = () if /^\s*$/ && $current{DEVICE}; -	$current{$1} = $2 if /^([^=]+)=(.*?)\s*$/; +    my %current;  +    foreach (run_program::get_stdout('serial_probe')) { +	if (/^\s*$/) { +	    $serialprobe{$current{DEVICE}} = { %current } if $current{DEVICE}; +	    %current = (); +	} elsif (/^([^=]+)=(.*?)\s*$/) { +	    $current{$1} = $2; +	}      }      foreach (values %serialprobe) { @@ -626,62 +1688,31 @@ sub probeSerialDevices() {  sub probeSerial($) { $serialprobe{$_[0]} }  sub hasModem($) { -    $serialprobe{$_[0]} and $serialprobe{$_[0]}{CLASS} eq 'MODEM' and $serialprobe{$_[0]}{DESCRIPTION}; +    $serialprobe{$_[0]} && $serialprobe{$_[0]}{CLASS} eq 'MODEM' && $serialprobe{$_[0]}{DESCRIPTION};  }  sub hasMousePS2 {      my $t; sysread(tryOpen($_[0]) || return, $t, 256) != 1 || $t ne "\xFE";  } -sub raidAutoStartIoctl() { -    sysopen(my $F, devices::make("md0"), 2) or return; -    ioctl $F, 2324, 0; -} - -sub raidAutoStartRaidtab { -    my (@parts) = @_; -    $::isInstall or return; -    require raid; -    #- faking a raidtab, it seems to be working :-))) -    #- (choosing any inactive md) -    raid::inactivate_all(); -    my $detect_one = sub { -	my ($device) = @_; -	my $free_md = devices::make(find { !raid::is_active($_) } map { "md$_" } 0 .. raid::max_nb()); -	output("/tmp/raidtab", "raiddev $free_md\n  device " . devices::make($device) . "\n"); -	log::l("raidAutoStartRaidtab: trying $device"); -	run_program::run('raidstart', '-c', "/tmp/raidtab", $free_md); -    }; -    $detect_one->($_->{device}) foreach @parts; - -    #- try again to detect RAID 10 -    $detect_one->($_) foreach raid::active_mds(); - -    unlink "/tmp/raidtab"; -} - -sub raidAutoStart { -    my (@parts) = @_; - -    log::l("raidAutoStart"); -    eval { modules::load('md') }; -    my %personalities = ('1' => 'linear', '2' => 'raid0', '3' => 'raid1', '4' => 'raid5'); -    raidAutoStartIoctl() or raidAutoStartRaidtab(@parts); -    foreach (1..2) { #- try twice for RAID 10 -	my @needed_perso = map {  -	    if_(/^kmod: failed.*md-personality-(.)/ || -		/^md: personality (.) is not loaded/, $personalities{$1}) } syslog() or last; -	eval { modules::load(@needed_perso) }; -	raidAutoStartIoctl() or raidAutoStartRaidtab(@parts); -    } +sub probeall_unavailable_modules() { +    map { +        my $driver = $_->{driver}; +        $driver !~ /:/ && +        !member($driver, 'hub', 'unknown', 'amd64_agp') && +        !modules::module_is_available($driver) ? +          $driver : +          @{[]}; +    } probeall();  } -sub is_a_recent_computer() { -    my ($frequence) = map { /cpu MHz\s*:\s*(.*)/ } cat_("/proc/cpuinfo"); -    $frequence > 600; +sub probeall_dkms_modules() { +    my @unavailable_modules = probeall_unavailable_modules() or return; +    require modalias; +    my $dkms_modules = modalias::parse_file_modules($::prefix . "/usr/share/ldetect-lst/dkms-modules.alias"); +    intersection([ keys(%$dkms_modules) ], \@unavailable_modules);  } -  sub usb_description2removable {      local ($_) = @_;      return 'camera' if /\bcamera\b/i; @@ -708,19 +1739,31 @@ sub suggest_mount_point {      my ($e) = @_;      my $name = $e->{media_type}; -    if (member($name, 'hd', 'fd')) { +    if (member($e->{media_type}, 'hd', 'fd')) {  	if (exists $e->{usb_driver}) { -	    return usb2removable($e) || 'removable'; -	} -	if (isZipDrive($e)) { +	    $name = usb2removable($e) || 'removable'; +	} elsif (isZipDrive($e)) {  	    $name = 'zip'; -	} elsif ($name eq 'fd') { +	} elsif ($e->{media_type} eq 'fd') {  	    $name = 'floppy';  	} else { -	    log::l("set_removable_mntpoints: don't know what to with hd $e->{device}"); +	    log::l("suggest_mount_point: do not know what to with hd $e->{device}");  	}      }      $name;  } +=back + +=head1 SEE ALSO + +See L<hardware_detection> for the overall view. + +=cut +  1; + +#- Local Variables: +#- mode:cperl +#- tab-width:8 +#- End: | 
