#!/usr/bin/perl
#
# LiveCD hardware detection script
# Copyright (C) 2002-2003, Jaco Greeff <jaco@linuxminicd.org>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Adapted for mklivecd from the MiniCD (http://www.linuxminicd.org> hwdetect script
#
# $Id: hwdetect.in,v 1.24 2003/10/06 12:30:35 jaco Exp $
#

use lib qw(/usr/lib/libDrakX);

use standalone;

use common;
use modules;

use fsedit;
use fs;

use harddrake::sound;
use keyboard;
use mouse;

use Xconfig::card;
use Xconfig::default;
use Xconfig::monitor;
use Xconfig::parse;
use Xconfig::resolution_and_depth;
use Xconfig::screen;
use Xconfig::xfree;
use Xconfig::xfree4;

use MDK::Common::File;
use MDK::Common::System;

### global constants
my $SCRIPT_DESC    = "hardware detection";
my $SCRIPT_NAME    = "hwdetect";
my $SCRIPT_VER     = "0.5.6";
my $DEF_KEYBOARD   = "us";

### screen constants
my $RES_COL        = 65;
my $ESC            = "\x1B[";
my $MOVE_TO_COL    = $ESC.$RES_COL."G";
my $COLOR_GREEN    = $ESC."1;32m";
my $COLOR_RED      = $ESC."1;31m";
my $COLOR_YELLOW   = $ESC."1;33m";
my $COLOR_NORMAL   = $ESC."0;39m";

### global varibles
my $debug       = 0;
my $probe       = 0;
my @all_devices = ();
my %cmdline;
my $owndevfsd   = 1;
my $ownprocusb  = 1;
my $haveswap    = 0;
my $haveusb     = 0;
my $havepcmcia  = 0;

sub rmws {
	my ($str) = @_;
	if (defined($str)) {
		$str =~ s/\s+$//;
		$str =~ s/^\s+//;
	}
	else {
		$str = "";
	}
	$str;
}

sub move_up_lines {
	my ($lines) = @_;
	print $ESC.$lines."A" if (!$debug);
}

sub move_down_lines {
	my ($lines) = @_;
	while ($lines) {
		print "\n" if (!$debug);
		$lines--;
	}
}

sub print_success {
	print $MOVE_TO_COL if (!$debug);
	print "[  ";
	print $COLOR_GREEN if (!$debug);
	print "OK";
	print $COLOR_NORMAL if (!$debug);
	print "  ]\n";
}

sub print_loaded {
	print $MOVE_TO_COL if (!$debug);
	print "[";
	print $COLOR_GREEN if (!$debug);
	print "LOADED";
	print $COLOR_NORMAL if (!$debug);
	print "]\n";
}

sub print_warning {
	print $MOVE_TO_COL if (!$debug);
	print "[ ";
	print $COLOR_YELLOW if (!$debug);
	print "WARN";
	print $COLOR_NORMAL if (!$debug);
	print " ]\n";
}

sub print_failed {
	print $MOVE_TO_COL if (!$debug);
	print "[";
	print $COLOR_RED if (!$debug);
	print "FAILED";
	print $COLOR_NORMAL if (!$debug);
	print "]\n";
}

sub print_hash {
	my ($hash) = @_;
	print "<device>\n";
	foreach my $key (keys %$hash) {
		my $value = $hash->{$key};
		print "\t<$key>$value</$key>\n";
	}
	print "</device>\n";
}

sub print_device {
	my ($module, $vendor, $desc) = @_;
	print "    ";
	$vendor = rmws($vendor);
	$desc = rmws($desc);
	print $vendor unless ($vendor eq "");
	print ", " unless (($vendor eq "") || ($desc eq ""));
	print $desc;
}

sub hwdetect_init {
	select(STDOUT);
	$| = 1;
	open STDERR, '>', "/dev/null";
	print "Starting $SCRIPT_DESC ($SCRIPT_NAME v$SCRIPT_VER): ";

	%cmdline = map {
		my ($name, $value) = split(/=/);
		$name => $value || 1;
	} split(/ /, cat_('/proc/cmdline'));

	# start devfsd (if not already running)
	my @devfspid = fuzzy_pidofs(qr/\bdevfsd\b/);
	$owndevfsd = 0 if (scalar(@devfspid) > 0);
	if ((-e '/dev/.devfsd') && (-e '/sbin/devfsd') && ($owndevfsd > 0)) {
		system("devfsd /dev 2>/dev/null");
	}

	print_success();
}

sub hwdetect_deinit {
	# kill devfsd (if we started it), it is to be started again
	# the in init scripts
	my @devfspid = fuzzy_pidofs(qr/\bdevfsd\b/);
	if (($owndevfsd > 0) && (scalar(@devfspid) > 0)) {
		system("kill -TERM ".$devfspid[0]." 2>/dev/null");
	}
	#print "Completing detection routines: ";
}

sub is_mounted {
	my ($point) = @_;
	my $mounted = 0;
	foreach my $mountline (cat_('/proc/mounts')) {
		my ($md, $mp, $mt, $mo, @me) = split(/ /, $mountline);
		$mounted = 1 if ($mp eq $point);
	}
	$mounted;
}

sub mount {
	my ($text, $opt, $type, $dev, $point) = @_;
	my $res = 0;
	my $mounted = is_mounted($point);

	print $text if ($text);
	$res = system("(mount $opt -t $type $dev $point) 2>/dev/null >/dev/null") if (!$mounted);
	if ($text) { $res ? print_failed() : print_success(); }
}

sub umount {
	my ($text, $point) = @_;
	my $res = 0;
	my $mounted = is_mounted($point);

	print $text if ($text);
	$res = system("(umount $point) 2>/dev/null >/dev/null") if ($mounted);
	if ($text) { $res ? print_failed() : print_success(); }
}

sub is_loaded {
	my ($module) = @_;
	my $loaded = 0;
	foreach my $modline (cat_('/proc/modules')) {
		my ($mod, @mode) = split(/ /, $modline);
		$loaded = 1 if ($mod eq $module);
	}
	$loaded;
}

sub modprobe {
	my ($opt, $module) = @_;
	my $res = 1;
	$res = system("(/sbin/modprobe $opt $module) 2>/dev/null >/dev/null") unless ($probe);
	$res;
}

sub hwdetect_get_mediatype {
	my ($media_type) = @_;
	my @rets = ();
	foreach my $device (@all_devices) {
		push(@rets, $device) if ($device->{media_type} =~ /$media_type/);
	}
	@rets;
}

sub hwdetect_get_bustype {
	my ($bus_type) = @_;
	my @rets = ();
	foreach my $device (@all_devices) {
		push(@rets, $device) if ($device->{bus} =~ /$bus_type/);
	}
	@rets;
}

sub hwdetect_loadmod {
	my ($module, $vendor, $desc) = @_;
	my $done = 0;
	print_device($module, $vendor, $desc);
	unless ($module =~ /unknown/) {
		if (is_loaded($module)) {
			print_warning();
		}
		else {
			my $res = modprobe("", $module);
			$res ? $probe ? print_warning : print_failed() : print_loaded();
		}
		$done++;
	}
	print "\n" unless($done);
}

sub read_proc_cpu {
	my (@cpus, $cpu);
	foreach (cat_("/proc/cpuinfo")) {
		if (/^processor/) { # ix86 specific
			push @cpus, $cpu if $cpu;
			$cpu = {};
		}
		/^([^\t]*).*:\s(.*)$/;
		$cpu->{$1} = $2 if $1;
	}
	push @cpus, $cpu;
	@cpus;
}

sub read_proc_mem {
	my ($mem);
	foreach (cat_("/proc/meminfo")) {
		if (/\:/) {
			/^([^\t]*).*:\s(.*)$/;
			$mem->{$1} = $2 if $1;
		}
	}
	$mem;
}

sub hwdetect_getmachine {
	print "Detecting architecture: ";
	my @probed_cpus = ();
	my $probed_mem = ();
	eval { @probed_cpus = read_proc_cpu() };
	eval { $probed_mem = read_proc_mem() };
	print scalar(@probed_cpus)." CPUs, ".rmws($probed_mem->{MemTotal})." memory found";
	scalar @probed_cpus && $probed_mem ? print_success() : print_failed();
	foreach my $cpu (@probed_cpus) {
		hwdetect_loadmod('unknown', $cpu->{vendor_id}, $cpu->{'model name'}." ".$cpu->{'cpu MHz'}."MHz ".$cpu->{'cache size'});
		print_hash($cpu) if ($debug);
	}
	if ($probed_mem) {
		hwdetect_loadmod('unknown', "Available Memory", rmws($probed_mem->{MemFree})." of ".rmws($probed_mem->{MemTotal}));
		hwdetect_loadmod('unknown', "Available Swap", rmws($probed_mem->{SwapFree})." of ".rmws($probed_mem->{SwapTotal})) if ($haveswap > 0);
		print_hash($probed_mem) if ($debug);
	}
}

sub hwdetect_probepci() {
	print "Probing PCI controllers: ";
	my @all_pci = ();
	my @all_bridge = ();
	my %pciid;
	eval { @all_pci = detect_devices::pci_probe() and print_success(); } or print_failed();
	foreach my $pci (@all_pci) {
		# make sure we display a single type only once
		unless (defined($pciid{$pci->{id}})) {
			$pciid{$pci->{id}} = 1;
			my ($probed_vendor, $probed_desc) = split(/\|/, $pci->{description});
			if ($pci->{media_type} =~ /SERIAL_USB/) {
				hwdetect_loadmod($pci->{driver}, $probed_vendor, $probed_desc);
				modules::add_probeall("usb-interface", $pci->{driver}) unless ($probe);
				print "modules::add_probeall('usb-interface', '".$pci->{driver}."')\n" if ($debug);
			}
			elsif ($pci->{media_type} =~ /STORAGE_SCSI/) {
				hwdetect_loadmod($pci->{driver}, $probed_vendor, $probed_desc);
				modules::add_probeall("scsi_hostadapter", $pci->{driver}) unless ($probe);
				print "modules::add_probeall('scsi_hostadapter', '".$pci->{driver}."')\n" if ($debug);
			}
			elsif ($pci->{media_type} =~ /BRIDGE_CARDBUS/) {
				hwdetect_loadmod($pci->{driver}, $probed_vendor, $probed_desc);
			}
			elsif ($pci->{media_type} =~ /BRIDGE_/) {
				push(@all_bridge, $pci);
			}
		}
		print_hash($pci) if ($debug);
	}

	# display all the bridges for which we are not loading drivers
	foreach my $bridge (@all_bridge) {
		my ($probed_vendor, $probed_desc) = split(/\|/, $bridge->{description});
		hwdetect_loadmod('unknown', $probed_vendor, $probed_desc);
	}

	# determine the type of busses we need to initialise now
	$havepcmcia = 1 if (is_loaded('pcmcia_core'));
	$haveusb = 1 if (is_loaded('usbcore'));
}

sub hwdetect_initpcmcia() {
	print "Initialising PCMCIA controllers: ";
	print_success();
	print "    Loading base PCMCIA modules: ";
	my $res = 0;
	my @modules = ('ds');
	$res += modprobe("-q", $_) foreach (@modules);
	$res ? $probe ? print_warning() : print_failed() : print_loaded();
}

sub hwdetect_initusb() {
	print "Initialising USB controllers: ";
	if (-e '/proc/bus/usb') {
		print_success();
		if (is_mounted('/proc/bus/usb')) {
			$ownprocusb = 0;
		}
		else {
			mount("    Mounting USB filesystem: ", "-n", "usbdevfs", "none", "/proc/bus/usb");
		}

		### load the usb modules that are not loaded automatically
		# "printer" should be in as well? (does cups stuff :(
		print "    Loading base USB modules: ";
		my $res = 0;
		my @modules = ('mousedev', 'keybdev', 'usb-storage', 'visor');
		$res += modprobe("-q", $_) foreach (@modules);
		$res ? $probe ? print_warning() : print_failed() : print_loaded();
	}
	else {
		print_failure();
	}
}

sub hwdetect_deinitusb() {
	if (-e '/proc/bus/usb') {
		umount("Unmounting USB filesystem", "/proc/bus/usb") if ($ownprocusb > 0);
	}
}

sub hwdetect_probeall {
	print "Probing devices: ";
	@all_devices = ();
	eval { detect_devices::probeSerialDevices(); };
	eval { @all_devices = detect_devices::probeall(); };
	scalar @all_devices ? print_success() : print_failed();
}

sub hwdetect_getkeyb {
	print "Selecting keyboard: ";
	my $probed_keyb = { KEYBOARD => $cmdline{'keyb'} };
	my $probed_desc = keyboard::keyboard2text($probed_keyb);
	unless (defined($probed_desc)) {
		$probed_keyb = { KEYBOARD => $DEF_KEYBOARD };
		$probed_desc = keyboard::keyboard2text($probed_keyb);
	}
	print_success();
	hwdetect_loadmod('unknown', "", $probed_desc);
	print_hash($probed_keyb) if ($debug);
	$probed_keyb;
}

sub hwdetect_getmouse {
	print "Detecting mouse: ";
	my $probed_vendor = "";
	my $probed_desc = "";
	my $device = undef;
	my $probed_mouse = undef;
	eval { $probed_mouse = mouse::detect() and print_success(); } or print_failed();
	if ($probed_mouse->{device} =~ /usbmouse/) {
		my @probed_mice = hwdetect_get_mediatype('Mouse');
		if (scalar @probed_mice) {
			$device = $probed_mice[0];
			($probed_vendor, $probed_desc) = split(/\|/, $device->{description});
		}
	}
	hwdetect_loadmod('unknown', $probed_vendor, $probed_mouse->{type}." ".$probed_mouse->{name}." ".$probed_desc);
	print_hash($probed_mouse) if ($debug);
	$probed_mouse;
}

sub hwdetect_getvideo {
	print "Detecting video cards: ";
	my @probed_cards = ();
	eval { @probed_cards = Xconfig::card::probe(); };
	print scalar(@probed_cards)." found";
	scalar @probed_cards ? print_success() : print_failed();
	my $probed_card = undef;
	foreach my $card (@probed_cards) {
		my ($probed_vendor, $probed_desc) = split(/\|/, $card->{description});
		hwdetect_loadmod('unknown', $probed_vendor, $card->{card_name});
		$probed_card = $card unless ($probed_card); # one card only :(
		print_hash($card) if ($debug);
	}
	$probed_card;
}

sub hwdetect_getmonitor {
	my ($rawX) = @_;
	print "Selecting monitor: ";
	my $probed_monitor = undef;

	# try to get a monitor via DDC
	eval { $probed_monitor = Xconfig::monitor::from_raw_X($rawX); };
	if (!$probed_monitor->{HorizSync}) {
		# just grab a good default
		my $def_monitor = undef;
		eval { $def_monitor = Xconfig::monitor::good_default_monitor(); };
		my $monitors = Xconfig::monitor::monitors();
		my %h_monitors = map {; "$_->{VendorName}|$_->{ModelName}" => $_ } @$monitors;
		$probed_monitor = $h_monitors{$def_monitor};
	}
	eval { $rawX->set_monitors($probed_monitor) and print_success(); } or print_failure();
	hwdetect_loadmod('unknown', $probed_monitor->{VendorName}, $probed_monitor->{ModelName}, 0);
	print_hash($probed_monitor) if ($debug);
	$probed_monitor;
}

sub hwdetect_getxfree {
	my $def_keyboard = hwdetect_getkeyb();
	my $def_mouse = hwdetect_getmouse();
	my $def_card = hwdetect_getvideo();
	my $rawX = Xconfig::default::configure($def_keyboard, $def_mouse);
	my $def_monitor = hwdetect_getmonitor($rawX);

	Xconfig::card::to_raw_X($def_card, $rawX);
	Xconfig::screen::configure($rawX, $def_card);
	Xconfig::resolution_and_depth::configure(undef, $rawX, $def_card, $def_monitor, 1);

	unless ($probe) {
		print "Writing X configuration: ";
    		eval { $rawX->write() and print_success(); } or print_failed();
	}
}

sub hwdetect_getsound {
	print "Detecting sound cards: ";
	my @probed_cards = ();
	eval { @probed_cards = hwdetect_get_mediatype('MULTIMEDIA_AUDIO'); };
	print scalar(@probed_cards)." found";
	scalar @probed_cards ? print_success() : print_failed();
	my $num = 0;
	foreach my $card (@probed_cards) {
		my ($probed_vendor, $probed_desc) = split(/\|/, $card->{description});
		modules::add_alias("sound-slot-$num", $card->{driver}) unless ($probe);
		print "modules::add_alias('sound-slot-$num', '".$card->{driver}."')\n" if ($debug);
		$num++;
		hwdetect_loadmod($card->{driver}, $probed_vendor, $probed_desc);
		print_hash($card) if ($debug);
	}
}

sub hwdetect_getnetwork {
	print "Detecting network cards: ";
	my @probed_cards = ();
	eval { @probed_cards = hwdetect_get_mediatype('NETWORK_ETHERNET|NETWORK_OTHER'); };
	print scalar(@probed_cards)." found";
	scalar @probed_cards ? print_success() : print_failed();
	my $num = 0;
	foreach my $card (@probed_cards) {
		my ($probed_vendor, $probed_desc) = split(/\|/, $card->{description});
		modules::add_alias("eth$num", $card->{driver}) unless ($probe);
		print "modules::add_alias('eth$num', '".$card->{driver}."')\n" if ($debug);
		$num++;
		hwdetect_loadmod($card->{driver}, $probed_vendor, $probed_desc);
		print_hash($card) if ($debug);
	}
}

sub hwdetect_getserial {
	print "Detecting serial devices: ";
	my @probed_devices = ();
	print scalar(@probed_devices)." found";
	print " (not implemented)";
	print_warning();
}

sub hwdetect_getusb {
	print "Detecting USB devices: ";
	my @probed_devices = ();
	eval { @probed_devices = hwdetect_get_bustype('USB'); };
	my @usable_devices = ();
	foreach my $device (@probed_devices) {
		unless (($device->{media_type} =~ /^Hub/) ||
			($device->{media_type} =~ /Mouse/ ||
			($device->{description} =~ /Hub/))) {
			push(@usable_devices, $device);
		}
	}
	print scalar(@usable_devices) . " found";
	scalar @probed_devices ? print_success() : print_failed();
	foreach my $device (@usable_devices) {
		my ($probed_vendor, $probed_desc) = split(/\|/, $device->{description});
		hwdetect_loadmod($device->{driver}, $probed_vendor, $probed_desc);
		print_hash($device) if ($debug);
	}
}

sub hwdetect_all {
	print_hash($_) foreach (@all_devices);
}

sub hwdetect_readconf {
	print "Reading modules configuration: ";
	modules::mergein_conf('/etc/modules.conf') if -r '/etc/modules.conf';
	print_success();
}

sub hwdetect_writeconf {
	print "Writing modules configuration: ";
	modules::write_conf("");
	print_success();
}

# descriptive name, type (or auto), mount defaults
my %fstypes = (
	0x0   => ['Empty'                                   ,''        ],
	0x183 => ['Journalised FS: ReiserFS'                ,'reiserfs'],
	0x283 => ['Journalised FS: XFS'                     ,'xfs'     ],
	0x383 => ['Journalised FS: JFS'                     ,'jfs'     ],
	0x483 => ['Journalised FS: ext3'                    ,'ext3'    ],
	0x1   => ['DOS 12-bit FAT'                          ,'ufs'     ],
	0x2   => ['XENIX root'                              ,'ufs'     ],
	0x3   => ['XENIX /usr'                              ,''        ],
	0x4   => ['DOS 16-bit FAT (up to 32M)'              ,'ufs'     ],
	0x5   => ['DOS 3.3+ Extended Partition'             ,''        ],
	0x6   => ['DOS FAT16'                               ,'ufs'     ],
	0x7   => ['NTFS'                                    ,'ntfs'    ],
	0x8   => ['OS/2 (v1.0-1.3 only)'                    ,'ufs'     ],
	0x9   => ['AIX data partition'                      ,''        ],
	0xa   => ['OS/2 Boot Manager'                       ,''        ],
	0xb   => ['FAT32'                                   ,'vfat'    ],
	0xc   => ['Win98 FAT32, LBA-mapped'                 ,'vfat'    ],
	0xe   => ['Win95: DOS 16-bit FAT, LBA-mapped'       ,'vfat'    ],
	0xf   => ['Win95: Extended partition, LBA-mapped'   ,''        ],
	0x10  => ['OPUS (?)'                                ,''        ],
	0x11  => ['Hidden DOS 12-bit FAT'                   ,''        ],
	0x12  => ['Compaq/HP config partition'              ,''        ],
	0x14  => ['Hidden DOS 16-bit FAT <32M'              ,''        ],
	0x16  => ['Hidden DOS 16-bit FAT >=32M'             ,''        ],
	0x17  => ['Hidden IFS (e.g., HPFS)'                 ,''        ],
	0x18  => ['AST Windows swapfile'                    ,''        ],
	0x1b  => ['Hidden WIN95 OSR2 32-bit FAT'            ,'vfat'    ],
	0x1c  => ['Hidden WIN95 OSR2 32-bit FAT, LBA-mapped','vfat'    ],
	0x1e  => ['Hidden FAT95'                            ,'vfat'    ],
	0x22  => ['Used for Oxygen Extended Partition Table',''        ],
	0x24  => ['NEC DOS 3.x'                             ,''        ],
	0x35  => ['JFS (OS/2)'                              ,''        ],
	0x38  => ['THEOS ver 3.2 2gb partition'             ,''        ],
	0x39  => ['THEOS ver 4 spanned partition'           ,''        ],
	0x3a  => ['THEOS ver 4 4gb partition'               ,''        ],
	0x3b  => ['THEOS ver 4 extended partition'          ,''        ],
	0x3c  => ['PartitionMagic recovery partition'       ,''        ],
	0x40  => ['Venix 80286'                             ,''        ],
	0x41  => ['Linux/MINIX (sharing disk with DRDOS)'   ,''        ],
	0x42  => ['Windows Dynamic Partition'               ,''        ],
	0x43  => ['Linux native (sharing disk with DRDOS)'  ,''        ],
	0x45  => ['EUMEL/Elan'                              ,''        ],
	0x46  => ['EUMEL/Elan 0x46'                         ,''        ],
	0x47  => ['EUMEL/Elan 0x47'                         ,''        ],
	0x48  => ['EUMEL/Elan 0x48'                         ,''        ],
	0x4d  => ['QNX4.x'                                  ,''        ],
	0x4e  => ['QNX4.x 2nd part'                         ,''        ],
	0x4f  => ['QNX4.x 3rd part / Oberon partition'      ,''        ],
	0x50  => ['OnTrack Disk Manager (older versions) RO',''        ],
	0x51  => ['Novell'                                  ,''        ],
	0x52  => ['CP/M / Microport SysV/AT'                ,''        ],
	0x53  => ['Disk Manager 6.0 Aux3'                   ,''        ],
	0x54  => ['Disk Manager 6.0 Dynamic Drive Overlay'  ,''        ],
	0x55  => ['EZ-Drive'                                ,''        ],
	0x56  => ['Golden Bow VFeature Partitioned Volume'  ,''        ],
	0x57  => ['DrivePro'                                ,''        ],
	0x5c  => ['Priam EDisk'                             ,''        ],
	0x61  => ['SpeedStor'                               ,''        ],
	0x63  => ['Unix System V (SCO, ...), Mach, GNU Hurd',''        ],
	0x64  => ['PC-ARMOUR protected partition'           ,''        ],
	0x65  => ['Novell Netware 3.xx or 4.xx'             ,''        ],
	0x67  => ['Novell'                                  ,''        ],
	0x68  => ['Novell 0x68'                             ,''        ],
	0x69  => ['Novell 0x69'                             ,''        ],
	0x70  => ['DiskSecure Multi-Boot'                   ,''        ],
	0x75  => ['IBM PC/IX'                               ,''        ],
	0x80  => ['MINIX until 1.4a'                        ,''        ],
	0x81  => ['MINIX since 1.4b, early Linux'           ,''        ],
	0x82  => ['Linux swap'                              ,'swap'    ],
	0x83  => ['Linux native'                            ,'ext2'    ],
	0x84  => ['OS/2 hidden C: drive'                    ,''        ],
	0x85  => ['Linux extended partition'                ,''        ],
	0x86  => ['Old Linux RAID partition superblock'     ,''        ],
	0x87  => ['NTFS volume set'                         ,''        ],
	0x8a  => ['Linux Kernel Partition (AiR-BOOT)'       ,''        ],
	0x8e  => ['Linux Logical Volume Manager partition'  ,''        ],
	0x93  => ['Amoeba'                                  ,''        ],
	0x94  => ['Amoeba bad block table'                  ,''        ],
	0x99  => ['DCE376 logical drive'                    ,''        ],
	0xa0  => ['IBM Thinkpad hibernation partition'      ,''        ],
	0xa5  => ['BSD/386, 386BSD, NetBSD, FreeBSD'        ,''        ],
	0xa6  => ['OpenBSD'                                 ,''        ],
	0xa7  => ['NEXTSTEP'                                ,''        ],
	0xa9  => ['NetBSD'                                  ,''        ],
	0xaa  => ['Olivetti Fat 12 1.44Mb Service Partition',''        ],
	0xb7  => ['BSDI filesystem'                         ,''        ],
	0xb8  => ['BSDI swap partition'                     ,''        ],
	0xbe  => ['Solaris boot partition'                  ,''        ],
	0xc0  => ['CTOS / REAL/32 secure small partition'   ,''        ],
	0xc1  => ['DRDOS/secured (FAT-12)'                  ,''        ],
	0xc4  => ['DRDOS/secured (FAT-16, < 32M)'           ,''        ],
	0xc6  => ['DRDOS/secured (FAT-16, >= 32M)'          ,''        ],
	0xc7  => ['Windows NT corrupted NTFS volume'        ,''        ],
	0xcb  => ['reserved for DRDOS/secured (FAT32)'      ,''        ],
	0xcc  => ['reserved for DRDOS/secured (FAT32, LBA)' ,''        ],
	0xcd  => ['CTOS Memdump?'                           ,''        ],
	0xce  => ['reserved for DRDOS/secured (FAT16, LBA)' ,''        ],
	0xd0  => ['REAL/32 secure big partition'            ,''        ],
	0xd1  => ['Old Multiuser DOS secured FAT12'         ,''        ],
	0xd4  => ['Old Multiuser DOS secured FAT16 <32M'    ,''        ],
	0xd5  => ['Old Multiuser DOS secured extended'      ,''        ],
	0xd6  => ['Old Multiuser DOS secured FAT16 >=32M'   ,''        ],
	0xd8  => ['CP/M-86'                                 ,''        ],
	0xdb  => ['Digital Research CP/M'                   ,''        ],
	0xdd  => ['Hidden CTOS Memdump?'                    ,''        ],
	0xe1  => ['DOS access partition'                    ,''        ],
	0xe3  => ['DOS R/O or SpeedStor'                    ,''        ],
	0xe4  => ['16-bit FAT extended partition <1024 cyl.',''        ],
	0xeb  => ['BeOS'                                    ,''        ],
	0xee  => ['EFI GPT'                                 ,''        ],
	0xef  => ['EFI (FAT-12/16/32)'                      ,''        ],
	0xf1  => ['SpeedStor 0xf1'                          ,''        ],
	0xf2  => ['DOS 3.3+ secondary partition'            ,''        ],
	0xf4  => ['SpeedStor large partition'               ,''        ],
	0xf5  => ['Prologue multi-volume partition'         ,''        ],
	0xfd  => ['Linux RAID'                              ,''        ],
	0xfe  => ['SpeedStor >1024 cyl'                     ,''        ],
	0xff  => ['Xenix Bad Block Table'                   ,''        ]
);

# these are default options for fstab
my $fsopt         = 'noauto,user,ro,exec';
my $fsopt_persist = 'user,rw,exec';

# type => (allow persistent, ext. opts)
my %fsopts = (
	'ext2'     => [1,     undef],
	'ext3'     => [1,     undef],
	'jfs'      => [1,     undef],
	'ntfs'     => [undef, 'iocharset=iso8859-1,umask=0'],
	'reiserfs' => [1,     'notail,noatime'],
	'swap'     => [undef, 'defaults'],
	'ufs'      => [1,     'iocharset=iso8859-1,umask=0'],
	'vfat'     => [1,     'iocharset=iso8859-1,codepage=850,umask=0'],
	'xfs'      => [1,     undef]
);

sub hwdetect_getpersistent {
	my (%devs) = @_;
	print "    Finding persistent partitions: ";
	my $mymnt = "/tmp/hwdetect.mnt";
	my $persist = "$mymnt/LIVECD.MNT";
	system("mkdir -p $mymnt 2>/dev/null");
	foreach my $dev (keys %devs) {
		if ((-e $mymnt) && $devs{$dev}->{persist}) {
			mount(undef, "", $devs{$dev}->{type}, $devs{$dev}->{devfs}, $mymnt);
			if (is_mounted($mymnt)) {
				if (-e $persist) {
					my %popts = map {
						chomp;
						my ($name, $value) = split(/=/);
						$name => $value || 1;
					} split(/ /, cat_($persist));
					if ($popts{mount}) {
						$devs{$dev}->{mount} = $popts{mount};
						$devs{$dev}->{opt} = $fsopt_persist;
						$devs{$dev}->{persisted} = 1;
					}
				}
				umount(undef, $mymnt);
			}
		}
	}
	system("rm -rf $mymnt 2>/dev/null");
	print_success();
}

sub hwdetect_getdisks {
	print "Detecting disk drives/partitions: ";
	my %all_devs = ();
	my $all_hdds = fsedit::get_hds({}, undef);
	fs::get_raw_hds('', $all_hdds);
	if ($debug) {
		foreach my $key (keys %$all_hdds) {
			print $key . " ->\n";
			my @disks = @{$all_hdds->{$key}};
			foreach my $disk (@disks) {
				print "\tdisk:\n";
				foreach my $diskkey (keys %$disk) {
					print "\t\t" . $diskkey . " -> " . $disk->{$diskkey} . "\n";
				}
			}
		}
	}

	### do all the cd, floppy stuff
	foreach my $disk (@{$all_hdds->{raw_hds}}) {
		if ($debug) {
			print "removable: " . $disk->{info} . "\n";
			print "\t/dev/" . $disk->{devfs_device} . " on /mnt/" . $disk->{device} . "\n";
		}
		unless ($disk->{media_type} =~ /^hd/) {
			if ($disk->{media_type} =~ /^fd|^cdrom/) {
				my $dev = $disk->{device};
				my $extopts = "--,iocharset=iso8859-1,codepage=850,umask=0";
				$extopts = $extopts.",nohide" if ($disk->{media_type} =~ /^cdrom/);
				$all_devs{$dev}->{info}   = $disk->{media_type} . ": " . $disk->{info};
				$all_devs{$dev}->{dev}    = "/dev/".$disk->{device};
				$all_devs{$dev}->{devfs}  = "/dev/".$disk->{devfs_device};
				$all_devs{$dev}->{media}  = $disk->{media_type};
				$all_devs{$dev}->{mount}  = "/mnt/".$disk->{device};
				$all_devs{$dev}->{type}   = "auto";
				$all_devs{$dev}->{opt}    = $fsopt;
				$all_devs{$dev}->{extopt} = $extopts;
			}
		}
	}

	### do the hard disks
	foreach my $disk (@{$all_hdds->{hds}}) {
		if ($debug) {
			print "disk: " . $disk->{info} . "\n";
			print "\tprimary:\n";
			foreach my $prim (keys %{$disk->{primary}}) {
				unless ($prim =~ /normal/ || $prim =~ /extended/ || $prim =~ /raw/) {
					print "\t\t" . $prim . " -> " . $disk->{primary}->{$prim} . "\n";
				}
			}
		}

		if ($debug) {
			print "\t\tnormal:\n";
			foreach my $norm (@{$disk->{primary}->{normal}}) {
				print "\t\t\t" . $norm . "\n";
			}
			print "\t\textended:\n";
			foreach my $extkey (%{$disk->{primary}->{extended}}) {
				print "\t\t\t" . $extkey . " -> " . $disk->{primary}->{extended}->{$extkey} . "\n";
			}
		}
		foreach my $raw (@{$disk->{primary}->{raw}}) {
			if ($debug) {
				print "\t\traw:" . "\n";
				foreach $rawkey (keys %$raw) {
					print "\t\t\t" . $rawkey . " -> " . $raw->{$rawkey} . "\n";
				}
			}
			unless ($raw->{size} =~ /^0/) {
				### the topmost extention might be set, which means
				### we are missing it (only do this if we cannot find it,
				### see it as a try 2)
				$raw->{type} = $raw->{type} & 0xFF unless ($fstypes{$raw->{type}});
				my @opts = @{$fsopts{$fstypes{$raw->{type}}[1]}};
				if ($raw->{devfs_device} && $fstypes{$raw->{type}}[1] && @opts) {
					my $dev = $raw->{device};
					$all_devs{$dev}->{info}    = "/dev/" . $raw->{device} . ", size=" . $raw->{size} . ", type=" . $raw->{type} . ": " . $fstypes{$raw->{type}}[0] . " (primary)";
					$all_devs{$dev}->{dev}     = "/dev/" . $raw->{device};
					$all_devs{$dev}->{devfs}   = "/dev/".$raw->{devfs_device};
					$all_devs{$dev}->{size}    = $raw->{size};
					$all_devs{$dev}->{media}   = "hd";
					if ($raw->{type} =~ /^130/) {
						$haveswap = 1;
						$all_devs{$dev}->{mount} = "swap";
					}
					else {
						$all_devs{$dev}->{mount} = "/mnt/".$raw->{device};
						$all_devs{$dev}->{opt}     = $fsopt;
					}
					$all_devs{$dev}->{type}    = $fstypes{$raw->{type}}[1];
					$all_devs{$dev}->{extopt}  = $opts[1];
					$all_devs{$dev}->{persist} = $opts[0];
				}
			}
		}
		foreach my $ext (@{$disk->{extended}}) {
			if ($debug) {
				print "\textended: " . $ext->{info} . "\n";
				foreach my $key (keys %$ext) {
					unless ($key =~ /normal/ || $key =~ /extended/ || $key =~ /raw/) {
						print "\t\t" . $key . " -> " . $ext->{$key} . "\n";
					}
				}
				print "\t\tnormal:\n";
				foreach my $normkey (%{$ext->{normkey}}) {
					print "\t\t\t" . $normkey . " -> " . $disk->{primary}->{normal}->{$normkey} . "\n";
				}
				print "\t\textended:\n";
				foreach my $extkey (%{$ext->{extended}}) {
					print "\t\t\t" . $extkey . " -> " . $disk->{primary}->{extended}->{$extkey} . "\n";
				}
			}
			foreach my $raw (@{$ext->{raw}}) {
				if ($debug) {
					print "\t\traw:" . "\n";
					foreach $rawkey (keys %$raw) {
						print "\t\t\t" . $rawkey . " -> " . $raw->{$rawkey} . "\n";
					}
				}
				unless ($raw->{size} =~ /^0/) {
					### the topmost extention might be set, which means
					### we are missing it (only do this if we cannot find it,
					### see it as a try 2)
					$raw->{type} = $raw->{type} & 0xFF unless ($fstypes{$raw->{type}});
					my @opts = @{$fsopts{$fstypes{$raw->{type}}[1]}};
					if ($raw->{devfs_device} && $fstypes{$raw->{type}}[1] && @opts) {
						my $dev = $raw->{device};
						$all_devs{$dev}->{info}    = "/dev/" . $raw->{device} . ", size=" . $raw->{size} . ", type=" . $raw->{type} . ": " . $fstypes{$raw->{type}}[0] . " (extended)";
						$all_devs{$dev}->{dev}     = "/dev/" . $raw->{device};
						$all_devs{$dev}->{devfs}   = "/dev/".$raw->{devfs_device};
						$all_devs{$dev}->{size}    = $raw->{size};
						$all_devs{$dev}->{media}   = "hd";
						if ($raw->{type} =~ /^130/) {
							$haveswap = 1;
							$all_devs{$dev}->{mount} = "swap";
						}
						else {
							$all_devs{$dev}->{mount} = "/mnt/".$raw->{device};
							$all_devs{$dev}->{opt}     = $fsopt;
						}
						$all_devs{$dev}->{type}    = $fstypes{$raw->{type}}[1];
						$all_devs{$dev}->{extopt}  = $opts[1];
						$all_devs{$dev}->{persist} = $opts[0];
					}
				}
			}
		}
	}
	$num = 0;
	$num++ foreach (keys %all_devs);
	print "$num found";
	print_success();
	if ($num) {
		unless ($probe) {
			# get persistent partitions and set their mountpoints,
			# and relevant options
			hwdetect_getpersistent(%all_devs);

			# create mountpoints
			print "    Creating device mountpoints: ";
			my $cmd;
			foreach my $dev (keys %all_devs) {
				my $mount = $all_devs{$dev}->{mount};
				system("mkdir -p $mount 2>/dev/null");
			}
			print_success();

			# write /etc/fstab
			print "    Writing /etc/fstab: ";
			open FSTAB, '>>', '/etc/fstab';
			print FSTAB "\n### entries below this line were automatically added by $SCRIPT_NAME v$SCRIPT_VER\n";
			open INFO, '>', '/etc/livecd/hwdetect/mounts.cfg';
			foreach my $dev (sort keys %all_devs) {
				print FSTAB "\n# ".$all_devs{$dev}->{info};
				my $entry = "\n";
				$entry .= $all_devs{$dev}->{devfs}."\t";
				$entry .= $all_devs{$dev}->{mount}."\t";
				$entry .= $all_devs{$dev}->{type}."\t";
				my $opt = "";
				$opt .= $all_devs{$dev}->{opt} if ($all_devs{$dev}->{opt});
				$opt .= "," if ($all_devs{$dev}->{opt} && $all_devs{$dev}->{extopt});
				$opt .= $all_devs{$dev}->{extopt} if ($all_devs{$dev}->{extopt});
				$entry .= $opt."\t"."0 0\n";
				print FSTAB $entry;

				print INFO "$dev=";
				print INFO "|info=".$all_devs{$dev}->{info};
				print INFO "|dev=".$all_devs{$dev}->{dev};
				print INFO "|devfs=".$all_devs{$dev}->{devfs};
				print INFO "|mount=".$all_devs{$dev}->{mount};
				print INFO "|media=".$all_devs{$dev}->{media};
				print INFO "|size=".$all_devs{$dev}->{size} if ($all_devs{$dev}->{size});
				print INFO "|type=".$all_devs{$dev}->{type};
				print INFO "|persist=".$all_devs{$dev}->{persist} if ($all_devs{$dev}->{persist});
				print INFO "|persisted=".$all_devs{$dev}->{persisted} if ($all_devs{$dev}->{persisted});
				print INFO "|opt=".$all_devs{$dev}->{opt} if ($all_devs{$dev}->{opt});
				print INFO "|extopt=".$all_devs{$dev}->{extopt} if ($all_devs{$dev}->{extopt});
				print INFO "\n";
			}
			print FSTAB "\n";
			close FSTAB;
			close INFO;
			print_success();
		}
	}
}


# start all swap partitions
sub hwdetect_swapon {
	print "Activating swap partitions: ";
	system("swapon -a -e") ? print_failure() : print_success();
}


foreach (@ARGV) {
	if    (/^--debug/) { $debug = 1; }
	elsif (/^--probe/) { $probe = 1; }
	else  { die "ERROR: Unknown option '$_'"; }
}

hwdetect_init();
hwdetect_getdisks();
hwdetect_swapon() if ($haveswap > 0);
hwdetect_getmachine();
hwdetect_readconf();
hwdetect_probepci();
hwdetect_initpcmcia() if ($havepcmcia > 0);
hwdetect_initusb() if ($haveusb > 0);
hwdetect_probeall();
hwdetect_all() if ($debug);
hwdetect_getxfree();
hwdetect_getsound();
hwdetect_getnetwork();
#hwdetect_getserial();
hwdetect_getusb() if -e '/proc/bus/usb/devices';
hwdetect_writeconf() unless ($probe);
hwdetect_deinitusb() if ($haveusb > 0);
hwdetect_deinit();
