#!/usr/bin/perl
# -*- cperl -*-
#
# LiveCD hardware detection script
#
# Copyright (C) 2002-2004, Jaco Greeff <jaco@puxedo.org>
# Copyright (C) 2004, Tom Kelly  <tom_kelly33@yahoo.com>
#
#    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
# The latest version of this script can be found at http://livecd.berlios.de
#
# $Id: hwdetect.in,v 1.154 2005/03/03 16:34:27 tom_kelly33 Exp $
#

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

use Getopt::Long;
#use standalone;
$ENV{SHARE_PATH} ||= "/usr/share";

use common;
use do_pkgs;
use fsedit;
use fs;
use harddrake::sound;
use keyboard;
use modules;
use mouse;

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

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

### global constants
my $SCRIPT_NAME    = "hwdetect";
my $SCRIPT_VER     = "0.5.9-20060203";
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 variables
my @all_devices;

my %all_devs;
my %cmdline;
my %languages;
my %strings;

my $card;
my $debug         = 0;
my $disk;
my $probe         = 0;
my $fdisk;
my $prefix        = "";
my $cmdline;
my $owndevfsd     = 1;
my $ownprocusb    = 1;
my $haveswap      = 0;
my $haveusb       = 0;
my $havepcmcia    = 0;
my $kernel26      = 0;
my $kernel_ver    = "x.x.x";
my $lang 	  = 0;
my $module;
my $modules_conf; 
my $num;
my $numusb	  = 0;
my $numwin	  = 'c';
my $old_X;
my $usbfstab	  = 0;

### progress stuff
my $progress_now  = 6+7;
my $progress_full = 20;
my $progress_max  = 65534;
my $progress_on = 1;

sub get_exec { 
	my $ret = `$_[0]`; 
	chomp($ret); 
	$ret; 
}

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 getStr('OK');
	print $COLOR_NORMAL if !$debug;
	print "]\n";
}

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

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


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

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

sub print_hash {
	my ($hash, $pre, $in) = @_;
	$pre = "" unless defined($pre);
	print "$pre<device>\n" unless defined($in);
	foreach my $key (keys %$hash) {
		my $value = $hash->{$key};
		print "$pre\t<$key>";
		if (defined($value)) {
			print $value;
			if (ref($value) eq "HASH") {
				print_hash($value, "$pre\t", 1);
				print "$pre\t";
			}
			elsif (ref($value) eq "ARRAY") {
				if (scalar(@$value) > 0) {
					print "\n";
					foreach my $elem (@$value) {
						print "$pre\t$elem\n";
						if (ref($elem) eq "HASH") {
							print_hash($elem, "$pre\t", 1);
						}
					}
					print "$pre\t";
				}
			}
		}
		print "</$key>\n";
	}	
	print "$pre</device>\n" unless defined($in);
}

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 set_progress() {
	if ($progress_on &&
	    -e '/proc/splash'  &&
	    $cmdline{splash} =~ /silent/) {
		my $pos = ($progress_max*(++$progress_now+1))/$progress_full;
		system("echo 'show $pos' >/proc/splash");
	}
}

sub hwdetect_init() {
	select(STDOUT);
	$| = 1;
	open STDERR, '>', "/dev/null";

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

	# initialise our languages
	%strings = getStrings();
	%languages = getLanguages();
	$lang = getMyLang();
	
	# start now
	print getStr('script_start') . " ($SCRIPT_NAME v$SCRIPT_VER): ";

	# get kernel version
	$kernel_ver = get_exec("uname -r");
	my $major = get_exec(qq(expr "$kernel_ver" : "\\([^.]\\+\\)\\..*"));
	my $minor = get_exec(qq(expr "$kernel_ver" : "[^.]\\+\\.\\([^.]\\+\\)\\..*"));
	if ($major == 2 && $minor >= 5) {
		$kernel26 = 1;
	}

	if ($kernel26 ne "1") {
		# 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("PATH=/sbin devfsd /dev 2>/dev/null"); # funny arrangement to allow us to kill it later
		}
	}

	print_success();
	set_progress();
}

sub hwdetect_deinit() {
	if ($kernel26 ne "1") {
		# 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");
		}
	}
	set_progress();
}

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 $mounted = is_mounted($point);

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

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 $umodule = $module;
	$umodule =~ s/\-/\_/g;
	
	# for 2.4.x we probe usb_?hci, but ?hci is loaded
	# for 2.6.x we probe usb_?hci, but ?hci_hcd is loaded
	my $usbmod;
	if ($umodule =~ m/^usb\_/ && $umodule =~ m/ehci|ohci|uhci/) {
		$usbmod = $umodule;
		$usbmod =~ s/^usb\_//;
		$usbmod .= "_hcd" if $kernel26;
	}
	
	my $loaded = 0;
	foreach my $modline (cat_('/proc/modules')) {
		unless ($loaded) {
			my ($mod) = split(/ /, $modline);
			$loaded = 1 if $mod eq $module || $mod eq $umodule || defined$usbmod && $mod eq $usbmod;
		}
	}
	$loaded;
}

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

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, $delay) = @_;
	my $done = 0;
	print_device($module, $vendor, $desc);
	unless ($module =~ /unknown/ || $module =~ /\:/) {
		$delay = undef if defined($delay) && is_loaded($module);
		my $num = modprobe("", $module);
		sleep($delay) if defined($delay) && $delay;
		!$num ? $probe ? print_warning() : print_noload() : 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 = {};
		}
		if (/^([^\t]*).*:\s(.*)$/) {
              $cpu->{$1} = $2 if $1;
          }
	}
	push @cpus, $cpu;
	@cpus;
}

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

sub hwdetect_getmachine() {
	print getStr('arch_probe') . ": ";
	my @probed_cpus;
	my $probed_mem;
	eval { @probed_cpus = read_proc_cpu() };
	eval { $probed_mem = read_proc_mem() };
	print scalar(@probed_cpus) . " " . getStr('arch_cpus') . ", " . rmws($probed_mem->{MemTotal}) . " " . getStr('arch_mem_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', getStr('avail_mem'), rmws($probed_mem->{MemFree}) . " " . getStr('of_mem') . " " .  rmws($probed_mem->{MemTotal}));
		hwdetect_loadmod('unknown', getStr('avail_swap'), rmws($probed_mem->{SwapFree}) . " " . getStr('of_mem') . " " .  rmws($probed_mem->{SwapTotal})) if $haveswap > 0;
		print_hash($probed_mem) if $debug;
	}
	set_progress();
}

sub hwdetect_probepci() {
	print getStr('pci_probe') . ": ";
	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/ && !defined($cmdline{nousb})) {
				hwdetect_loadmod($pci->{driver}, $probed_vendor, $probed_desc, 5);
				$modules_conf->add_probeall("usb-interface", $pci->{driver}) unless $probe;
				print "\$modules_conf->add_probeall('usb-interface', '" . $pci->{driver} . "')\n" if $debug;
			}
			elsif ($pci->{media_type} =~ /STORAGE_SCSI/ && !defined($cmdline{noscsi})) {
				hwdetect_loadmod($pci->{driver}, $probed_vendor, $probed_desc);
				$modules_conf->add_probeall("scsi_hostadapter", $pci->{driver}) unless $probe;
				print "\$modules_conf->add_probeall('scsi_hostadapter', '" . $pci->{driver} . "')\n" if $debug;
			}
			elsif ($pci->{media_type} =~ /BRIDGE_CARDBUS/ && !defined($cmdline{nopcmcia})) {
				unless (is_loaded($pci->{driver})) {
					my %popts;
					foreach my $opt (cat_("/etc/sysconfig/pcmcia")) {
						chomp($opt);
						my ($name, $value) = split(/=/, $opt);
						$popts{$name} = $value || 1;
					}
					$popts{PCMCIA} = "yes";
					$popts{PCIC} = $pci->{driver};
					
					# this should be decent file operations!
					system(":> /etc/sysconfig/pcmcia");
					foreach my $opt (sort keys %popts) {
						system("echo '" . $opt . "=" . $popts{$opt} . "' >>/etc/sysconfig/pcmcia");
					}
				}
				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');
	set_progress();
}

sub hwdetect_initpcmcia() {
	if ($havepcmcia > 0 && !defined($cmdline{nopcmcia})) {
		print getStr('pcmcia_init') . ": ";
		print_success();
		print "    " . getStr('pcmcia_load') . ": ";
		my $num = 0;
		my @modules = ('ds');
		$num += modprobe("-q", $_) foreach @modules;
		!$num ? $probe ? print_warning() : print_failed() : print_loaded();
	}
	else {
		system("rm -rf /etc/rc.d/rc?.d/*pcmcia");
	}
	set_progress();
}

sub hwdetect_initusb() {
	if ($haveusb > 0 && !defined($cmdline{nousb})) {
		print getStr('usb_init') . ": ";
		if (-e '/proc/bus/usb') {
			print_success();
			if (is_mounted('/proc/bus/usb')) {
				$ownprocusb = 0;
				$usbfstab = 1;
			}
			else {
				$usbfstab = mount("    " . getStr('usb_mount') . ": ", "-n", "usbfs", "none", "/proc/bus/usb");
			}

			### load the usb modules that are not loaded automatically
			# "printer" should be in as well? (does cups stuff :(
			print "    " . getStr('usb_load') . ": ";
			my $num = 0;
			my @modules = $kernel26 ? ('input', 'usb-storage', 'visor', 'usbmouse', 'usbhid') : ('input', 'mousedev', 'keybdev', 'usb-storage', 'visor');
			$num += modprobe("-q", $_) foreach @modules;
			!$num ? $probe ? print_warning() : print_failed() : print_loaded();
		}
		else {
			print_failed();
		}
	}
	set_progress();
}

sub hwdetect_deinitusb() {
	if ($haveusb > 0 &&
		-e '/proc/bus/usb' && !defined($cmdline{nousb})) {
		umount(getStr('usb_umount'), "/proc/bus/usb") if $ownprocusb > 0;
	}
	set_progress();
}

sub hwdetect_probeall() {
	print getStr('all_probe') . ": ";
	@all_devices = ();
	eval { detect_devices::probeSerialDevices() };
	eval { @all_devices = detect_devices::probeall() };
	scalar @all_devices ? print_success() : print_failed();
	set_progress();
}

sub hwdetect_getsata() {
	my $sata;
	my $total = 0;
	my $res = "";

	# Return if noscsi
	if (defined($cmdline{noscsi})) {
		print getStr('skip_sata');
		print_success();
		return
	}
	my @satalist = qw(libata ata_piix sg 
		sata_nv sata_promise sata_sil sata_svw sata_via sata_vsc);

#        my @satalist = qw(libata sd_mod ata_piix scsi_mod sr_mod sg a100u2w
#                advansys aha152x aha1542 aic7xxx BusLogic fdomain gdth
#                megaraid sata_nv sata_promise sata_sil sata_svw sata_via sata_vsc);

	print getStr('mod_loaded') . ": ";	
	foreach $sata (@satalist) {
		$res = modprobe("", $sata);
		if ($res eq 0) {
			print "$sata "; 
			$total+=1;
		}
	}
	print "\nTotal SATA modules loaded = $total";
	print_success();
}

sub hwdetect_getkeyb() {
	print getStr('keyboard_probe') . ": ";
	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 getStr('mouse_probe') . ": ";
	my $probed_vendor = "";
	my $probed_desc = "";
	my $device;
	my $probed_mouse;
	eval { $probed_mouse = mouse::detect($modules_conf) 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});
		}
	}
	#elsif ($probed_mouse->{device} =~ /psaux/) {
	#	# http://lists.berlios.de/pipermail/minicd-scripts/2003-October/000229.html
	#	unless ($probed_mouse->{XMOUSETYPE} =~ /GlidePoint/) {
	#		$probed_mouse->{XMOUSETYPE} = $kernel26 ? 'ExplorerPS/2' : 'auto'; # X will auto-detect the protocol
	#		$probed_mouse->{nbuttons} = 4;  # get ZAxisMapping right (needed for wheel)
	#	}
	#}
	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 getStr('video_probe') . ": ";
	my @probed_cards;
	eval { @probed_cards = Xconfig::card::probe() };
	print scalar(@probed_cards) . " " . getStr('found');
	my $probed_card;
	if (scalar @probed_cards) {
		print_success();
		foreach $card (@probed_cards) {
			my ($probed_vendor, $_probed_desc) = split(/\|/, $card->{description});
			my $module = 'unknown';
			unless ($probed_card) { # one card only :(
				$probed_card = $card;
				if (defined($cmdline{xdrv})) {
					$probed_card->{Driver} = $cmdline{xdrv};
					$probed_card->{Driver2} = $probed_card->{Driver};
				}
				if ($probed_card->{Driver2} eq 'nvidia' &&
					-e "/usr/X11R6/lib/modules/drivers/nvidia_drv.o" &&
					-e "/usr/X11R6/lib/modules/extensions/libglx.so" &&
					(-e "/lib/modules/$kernel_ver/kernel/drivers/char/nvidia.o.gz" ||
					-e "/lib/modules/$kernel_ver/kernel/drivers/video/nvidia.ko" ||
					-e "/lib/modules/$kernel_ver/kernel/drivers/video/nvidia.o")) {
					$probed_card->{Driver} = $probed_card->{Driver2};
					$probed_card->{DRI_GLX_SPECIAL} = 1;
					$module = $probed_card->{Driver2};
					$modules_conf->set_alias("/dev/nvidia*", $module);
				}
				if ($probed_card->{Driver2} eq 'fglrx' &&
					-e "/usr/X11R6/lib/modules/dri/fglrx_dri.so" &&
					-e "/usr/X11R6/lib/modules/drivers/fglrx_drv.o" &&
					(-e "/lib/modules/$kernel_ver/kernel/drivers/char/fglrx.o.gz" ||
					 -e "/lib/modules/$kernel_ver/kernel/drivers/video/fglrx.ko" ||
					 -e "/lib/modules/$kernel_ver/kernel/drivers/video/fglrx.o")) {
					$probed_card->{Driver} = $probed_card->{Driver2};
					$probed_card->{use_DRI_GLX} = 1;
					$module = $probed_card->{Driver2};
				}
			}
			$card->{VideoRam} = $cmdline{xram} if defined($cmdline{xram});
			hwdetect_loadmod($module, $probed_vendor, $card->{card_name});
			print_hash($card) if $debug;
		}
	}
	else {
		print_warning();
		$probed_card = { 
			Driver      => 'fbdev', 
			description => 'Linux|Default Framebuffer Device', 
			VideoRam    => 4096
		};
		
		my ($probed_vendor, $_probed_desc) = split(/\|/, $card->{description});
		$probed_card->{Driver} = $cmdline{xdrv} if defined($cmdline{xdrv});
		$probed_card->{VideoRam} = $cmdline{xram} if defined($cmdline{xram});
		hwdetect_loadmod($module, $probed_vendor, $card->{card_name});
	}
	$probed_card;
}

# res => [ laptop (monitor), generic (monitor), x, y ]
my %generic_res = (
	'640x480'   => [ 'Generic|Flat Panel 1024x768',  'Generic|1024x768 @ 60 Hz',  640,  480  ],
	'800x600'   => [ 'Generic|Flat Panel 1024x768',  'Generic|1024x768 @ 60 Hz',  800,  600  ],
	'1024x768'  => [ 'Generic|Flat Panel 1024x768',  'Generic|1024x768 @ 60 Hz',  1024, 768  ],
	'1280x1024' => [ 'Generic|Flat Panel 1280x1024', 'Generic|1280x1024 @ 60 Hz', 1280, 1024 ],
	'1400x1050' => [ 'Generic|Flat Panel 1400x1050', 'Generic|1280x1024 @ 60 Hz', 1400, 1050 ],
	'1600x1400' => [ 'Generic|Flat Panel 1600x1200', 'Generic|1600x1200 @ 70 Hz', 1600, 1400 ]
);

sub hwdetect_getmonitors {
	my ($rawX) = @_;
	print getStr('monitor_probe') . ": ";
	my $probed_monitors;

        # try to get a monitor via DDC
	eval { 
		$probed_monitors = Xconfig::monitor::configure_auto_install($rawX, {});
	};
	if (int @$probed_monitors > 0) {
		print_success();
	} else {
		print_failed();
	}
	foreach my $monitor (@$probed_monitors) {
		hwdetect_loadmod('unknown', $monitor->{VendorName}, $monitor->{ModelName}, 0);
		print_hash($monitor) if $debug;
	}
	$probed_monitors;
}

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

	# config card and screen
	Xconfig::card::to_raw_X($def_card, $rawX);
	Xconfig::screen::configure($rawX, $def_card);

	# detect a resolution
	print getStr('resolution_probe') . ": ";
	$old_X->{default_depth} = defined($cmdline{xbpp}) ? $cmdline{xbpp} : 16;
	my $resolution_wanted;
	if (defined($cmdline{xres})) {
		$resolution_wanted->{Depth} = $old_X->{default_depth};
		$resolution_wanted->{X} = $generic_res{$cmdline{xres}}[2] || 1024;
		$resolution_wanted->{Y} = $generic_res{$cmdline{xres}}[3] || 768;
		$old_X->{resolution_wanted} = $resolution_wanted;
	}
        
	# set the desired res, and override if failed and we are not safe
	my $resolution;
	eval { $resolution = Xconfig::resolution_and_depth::configure_auto_install($rawX, $def_card, $def_monitors, $old_X) };
	if (!defined($resolution) && defined($resolution_wanted) && !defined($cmdline{safex})) {
		eval { $rawX->set_resolution($resolution_wanted) };
		eval { $resolution = $rawX->get_resolution };
	}

	# try to make sure we have at least a valid 800x600 before finally setting it
	if (!defined($resolution) ||
	    !defined($resolution->{X}) || !defined($resolution->{Y}) || !defined($resolution->{Depth}) ||
	    ($resolution->{X} < 800 && !defined($cmdline{xres}) && !defined($cmdline{xbpp}))) {
		$resolution->{X} = 800;
		$resolution->{Y} = 600;
		$resolution->{Depth} = 16;
	}
	eval { $rawX->set_resolution($resolution) };
	eval { $resolution = $rawX->get_resolution and print_success() } or print_failed();
	hwdetect_loadmod('unknown', $resolution->{X} . "x" . $resolution->{Y}, $resolution->{Depth} . " bpp", 0);

	unless ($probe) {
		print getStr('x_write') . ": ";
		eval { mouse::write(undef, $def_mouse) };
		eval { keyboard::write($def_keyboard) };
		eval { $rawX->write and print_success() } or print_failed();
	}
	set_progress();
}

sub hwdetect_getsound() {
	my @probed_cards;
	unless (defined($cmdline{nosound})) {
		print getStr('sound_probe') . ": ";
		eval { @probed_cards = hwdetect_get_mediatype('MULTIMEDIA_AUDIO') };
		print scalar(@probed_cards) . " " . getStr('found');
		scalar @probed_cards ? print_success() : print_failed();
		my $num = 0;
		foreach my $card (@probed_cards) {
			my ($probed_vendor, $probed_desc) = split(/\|/, $card->{description});
			my $sndnum = "sound$num";
			my $driver = $card->{driver};
			$driver = $cmdline{sound} if defined($cmdline{sound});
			$driver = $cmdline{$sndnum} if defined($cmdline{$sndnum});
			$modules_conf->set_alias("sound-slot-$num", $driver);
			print "\$modules_conf->set_alias('sound-slot-$num', '$driver')\n" if $debug;
			if ($card->{options} && $driver eq $card->{driver}) {
				$modules_conf->set_options($driver, $card->{options}); 
				print "\$modules_conf->set_options($driver, " . $card->{options} . ")\n" if $debug;
			}
			
			# sound card specifics not defaulted in MDK
			$modules_conf->set_options('snd-via82xx', 'index=0 dxs_support=2') if $driver =~ /via82xx/;
			$modules_conf->set_options('snd-card-intel8x0', 'snd_ac97_clock=41194') if $driver =~ /intel8x0/;
			
			$num++;
			# hwdetect_loadmod($card->{driver}, ...) makes alsa break since the init scripts
			# want to load the correct module.
			hwdetect_loadmod('unknown', $probed_vendor, $probed_desc);
			print_hash($card) if $debug;
		}
	}
	system("rm -rf /etc/rc.d/rc?.d/*alsa /etc/rc.d/rc?.d/sound") unless scalar(@probed_cards) || $probe;
	set_progress();
}

sub hwdetect_getnetwork() {
	my @probed_cards;
	unless (defined($cmdline{nonetwork})) {
		print getStr('network_probe') . ": ";
		eval { @probed_cards = hwdetect_get_mediatype('NETWORK_ETHERNET|NETWORK_OTHER') };
		print scalar(@probed_cards) . " " . getStr('found');
		scalar @probed_cards ? print_success() : print_failed();
		my $num = 0;
		foreach my $card (@probed_cards) {
			my ($probed_vendor, $probed_desc) = split(/\|/, $card->{description});
			my $ethnum = "eth$num";
			my $driver = $card->{driver};
			$driver = $cmdline{eth} if defined($cmdline{eth});
			$driver = $cmdline{$ethnum} if defined($cmdline{$ethnum});
			
			$modules_conf->set_alias("eth$num", $driver) unless $probe;
			print "\$modules_conf->set_alias('eth$num', '$driver')\n" if $debug;
			
			$num++;
			# don't load driver, only add to /etc/modules.conf (as with sound)
			hwdetect_loadmod('unknown', $probed_vendor, $probed_desc);
			print_hash($card) if $debug;
		}
		## Turn off IPV6
		if ($kernel26) {
			$modules_conf->set_alias('net-pf-10', 'off');
			print "\$modules_conf->set_alias('net-pf-10', 'off')\n" if $debug;
			print "IPV6 turned off \n";
		}
	}
	system("rm -rf /etc/rc.d/rc?.d/*network") unless scalar(@probed_cards) || $probe;
	set_progress();
}

sub hwdetect_getprinters() {
	print getStr('printer_probe') . ": ";
	my @probed_printers;
	#eval { require printer::detect; @probed_printers = local_detect() };
	print scalar(@probed_printers) . " " . getStr('found');
	scalar @probed_printers ? print_success() : print_failed();
	system("rm -rf /etc/rc.d/rc?.d/*cups") unless scalar(@probed_printers);
	foreach my $printer (@probed_printers) {
		print_hash($printer) if $debug;
	}
	set_progress();
}

sub hwdetect_getserial() {
	print getStr('serial_probe') . ": ";
	my @probed_devices;
	print scalar(@probed_devices) . " " . getStr('found');
	print " " . getStr('not_impl');
	print_warning();
	set_progress();
}

sub hwdetect_getusb() {
	if (-e '/proc/bus/usb/devices' && !defined($cmdline{nousb})) {
		print getStr('usb_probe') . ": ";
		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) . " " . getStr('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;
		}
	}
	set_progress();
}

sub hwdetect_printall() {
	if ($debug) {
		print_hash($_) foreach @all_devices;
	}
}

sub hwdetect_readconf() {
	print getStr('mod_read') . ": ";
	$modules_conf = modules::any_conf->read;
	print_success();
	set_progress();
}

sub hwdetect_writeconf() {
	unless ($probe) {
		print getStr('mod_write') . ": ";
		system(":> $prefix/etc/modules.conf"); # clear first
		system(":> $prefix/etc/modprobe.conf"); # clear first
		$modules::prefix = undef;
		$modules::prefix = $prefix;
		if ($kernel26) {
			modules::modprobe_conf::write($modules_conf);
		} else {
			modules::any_conf::write($modules_conf);
		}
		print_success();
	}
	set_progress();
}

# descriptive name, type (or auto), mount defaults
my %fstypes = (
	0x0   => [ 'Empty'                                   , ''        ],
	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'                               , 'vfat'    ],
	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_user   = 'user,exec';
my $fsopt_auto   = 'auto';
my $fsopt_noauto = 'noauto';
my $fsopt_ro     = 'ro';
my $fsopt_rw     = 'rw';
my $fsopt_fullrw = $fsopt_user . "," . $fsopt_rw;
my $fsopt_fullro = $fsopt_user . "," . $fsopt_ro;

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

sub hwdetect_raw_to_fs_description {
	my ($raw) = @_;
	my $pt_type = $raw->{pt_type};
	if ($pt_type == 0x83) {
		my $fs_type = $raw->{fs_type};
		if ($fs_type eq 'reiserfs') {
			return 'Journalised FS: ReiserFS';
		} elsif ($fs_type eq 'xfs') {
			return 'Journalised FS: XFS';
		} elsif ($fs_type eq 'jfs') {
			return 'Journalised FS: JFS';
		} elsif ($fs_type eq 'ext3') {
			return 'Journalised FS: ext3';
		}
	}
    return $fstypes{$pt_type}[0];
}

sub hwdetect_getdisks_add_hdd {
    my	($raw, $fsopt_set, $comment) = @_;
    if ($raw->{size} != 0) {
	my $fsType = $raw->{fs_type};
	if ($fsopts{$fsType}) {
	    my @opts = @{$fsopts{$fsType}};
	    my $dev = $raw->{device};
	    my $info = "/dev/" . $raw->{device} . ", size=" . $raw->{size} . ", type=" . $raw->{pt_type} . ": "
		. hwdetect_raw_to_fs_description($raw);
	    if ($comment) {
		$info .= ' (' . $comment . ')';
	    }
	    $all_devs{$dev}{info}    	= $info;
	    $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 ($fsType eq 'swap') {
		$haveswap = 1;
		$all_devs{$dev}{mount} = "swap";
	    } else {
		$all_devs{$dev}{mount} = "/mnt/" . $raw->{device};
		if ($disk->{usb_media_type} =~ /^Mass Storage/ || $disk->{info} =~ /Flash/) {
		    $all_devs{$dev}{opt} = $fsopt_auto . "," . $fsopt_fullrw;
		    $all_devs{$dev}{usb} = 1;
		    $all_devs{$dev}{mount} = "/mnt/usb" . $numusb;
	    	    $numusb++;
		} elsif (defined($opts[2])) {
		    $all_devs{$dev}{opt} = ($cmdline{fstab} =~ /noauto/)
			? $fsopt_fullro . "," . $fsopt_noauto
			: $fsopt_fullro . "," . $fsopt_auto;
		} else {
		    $all_devs{$dev}{opt} = $fsopt_set;
		}
		if ($fsType =~ /ntfs|vfat/) {
		    $all_devs{$dev}{mount} = "/mnt/win_" . $numwin++;
		}
	    }
	    $all_devs{$dev}{type} 	= $fsType;
	    $all_devs{$dev}{extopt}  	= $opts[1];
	    $all_devs{$dev}{persist}	= $opts[0];
	}
    }
}

sub hwdetect_getdisks() {
	print getStr('disk_probe') . ": ";
	my $fsopt_set = $fsopt_user;
	$fsopt_set .= $cmdline{fstab} =~ /ro/ ? "," . $fsopt_ro : "," . $fsopt_rw;
	$fsopt_set .= $cmdline{fstab} =~ /noauto/ ? "," . $fsopt_noauto : "," . $fsopt_auto;
	my $all_hdds = fsedit::get_hds({}, undef);
	fs::get_raw_hds('', $all_hdds);
	
	if ($debug) {
		print "\n hwdetect_getdisks \n";
		foreach my $key (keys %$all_hdds) {
			print $key . " ->\n";
			my @disks = @{$all_hdds->{$key}};
			foreach my $disk (@disks) {
				print "\tdisk:\n";
				print_hash($disk);
			}
		}
	}

	### do all the cd, floppy stuff
	my $cdsymlinks = "/etc/udev/scripts/cdsymlinks.sh";
	my $numfloppy = ""; # floppy, floppy1
	my $numcd = ""; # cdrom, cdrom1
	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};
				if ($disk->{media_type} =~ /^fd/) {
					$all_devs{$dev}{mount}  = "/mnt/floppy$numfloppy";
					$numfloppy++;
					if ($numfloppy eq "1") { $numfloppy++ } # skip 1
				}
				else {
					$all_devs{$dev}{mount}  = "/mnt/cdrom$numcd";
					$numcd++;
					if ($numcd eq "1") { $numcd++ }  # skip 1
				}
				$all_devs{$dev}{type}   = "auto";
				if ($disk->{media_type} =~ /^fd|^cdrom/) {
					$all_devs{$dev}{opt} = $fsopt_fullro.",".$fsopt_noauto;
					if ($disk->{media_type} =~ /^fd/) {
						$all_devs{$dev}{opt} = $fsopt_fullrw . "," . $fsopt_noauto;
					}
				}
				else {
					$all_devs{$dev}{opt} = $fsopt_fullrw;  ##### . "," . $fsopt_noauto;
				}
				$all_devs{$dev}{extopt} = $extopts;
				$all_devs{$dev}{supermount} = 0;
			}
		}
	}

	### update cdsymlinks
	if ( -e "$cdsymlinks" ) {
		eval { "$cdsymlinks" }
	}

	### 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 my $rawkey (keys %$raw) {
					print "\t\t\t" . $rawkey . " -> " . $raw->{$rawkey} . "\n";
				}
			}
			hwdetect_getdisks_add_hdd($raw, $fsopt_set, 'primary');
		}
		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 my $rawkey (keys %$raw) {
						print "\t\t\t" . $rawkey . " -> " . $raw->{$rawkey} . "\n";
					}
				}
				hwdetect_getdisks_add_hdd($raw, $fsopt_set, 'extended');
			}
		}
	}
	
	### do the lvm disks
	foreach my $vol (@{$all_hdds->{lvms}}) {
		my $name = $vol->{VG_name};
		foreach my $disk ($vol->{primary}{normal}) {
			foreach my $lvm (@$disk) {
				hwdetect_getdisks_add_hdd($lvm, $fsopt_set, $name);
			}
		}
	}
	
	$num = keys %all_devs;
	print "$num " . getStr('found');
	print_success();
	set_progress();
}


# write the disk stuff
sub hwdetect_writedisks() {
	print "\n  hwdetect_writedisks \n";
	unless ($probe) {
		print getStr('disk_write') . ":\n";
		# get persistent partitions and set their mountpoints,
		# and relevant options
		# NO hwdetect_getpersistent(%all_devs);

		# create mountpoints
		print "    " . getStr('disk_create') . ": ";
		foreach my $dev (sort keys %all_devs) {
			system("mkdir -p $prefix/" . $all_devs{$dev}{mount} . " 2>/dev/null");
		}
		print_success();

		# write /etc/fstab
		mkdir_p("$prefix/etc/livecd/hwdetect/");
		print "    Writing $prefix/etc/fstab: ";
          my $FSTAB;
		open $FSTAB, '>', "$prefix/etc/fstab";
		print $FSTAB "\n### " . getStr('disk_fstab_info') . " $SCRIPT_NAME v$SCRIPT_VER\n";
		open my $INFO, '>', "$prefix/etc/livecd/hwdetect/mounts.cfg";
		print $FSTAB "# ROOT\n/dev/root\t/\trootfs\tdefaults\t0 0\n";
		print $FSTAB "# USB\nnone\t/proc/bus/usb\tusbfs\tdefaults\t0 0\n" if $usbfstab;
		print $FSTAB "# PROC\nnone\t/proc\t\tproc\t\tdefaults\t0 0\n";
		print $FSTAB "# PTS\nnone\t/dev/pts\tdevpts\tmode=0620\t0 0\n";

		foreach my $dev (sort keys %all_devs) {
			print $FSTAB "\n# " . $all_devs{$dev}{info};
			if ($all_devs{$dev}{supermount}) {
				my $entry = "\n";
				$entry .= "none\t";
				$entry .= $all_devs{$dev}{mount} . "\t";
				$entry .= "supermount\t";
				my $opt = "dev=" . $all_devs{$dev}{dev} . ",";
				# my $opt = "dev=" . $all_devs{$dev}{dev} . ",fs=udf:iso9660,";
				$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;
			}
			else {
				my $entry = "\n";
				$entry .= $all_devs{$dev}{dev} . "\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;
			}

			if ($all_devs{$dev}{loop}) {
				my $entry = "# " . getStr('disk_fstab_loop') . "\n";
				$entry .= $all_devs{$dev}{mount} . "/livecd.img\t";
				$entry .= $all_devs{$dev}{loop} . "\t";
				$entry .= "ext2\t";
				$entry .= "loop," . $fsopt_fullrw . "\t";
				$entry .= "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 "|usb=" . $all_devs{$dev}{usb} if $all_devs{$dev}{usb};
			print $INFO "|loop=" . $all_devs{$dev}{loop} if $all_devs{$dev}{loop};
			print $INFO "|supermount=" . $all_devs{$dev}{supermount} if $all_devs{$dev}{supermount};
			print $INFO "\n";
		}
		print $FSTAB "\n";
		close $FSTAB;
		close $INFO;
		print_success();
	}
	set_progress();
}

# start all swap partitions
sub hwdetect_swapon() {
	if ($haveswap > 0) {
		print getStr('swap_activate') . ": ";
		system("/sbin/swapon -a -e") ? print_failed() : print_success();
	}
	set_progress();
}

sub nothing () {};

sub help() {
	print <<EOF;
Help for $SCRIPT_NAME Version $SCRIPT_VER
Hardware detection / configuration program.
Options are --help	Prints this message
--version 		Prints program name and version
--debug			Debug mode
--probe			Probe mode
--fdisk			Disk mode
--prefix		Specify a prefix
EOF
}

sub version () {
	print "$SCRIPT_NAME $SCRIPT_VER\n";
}

MAIN: { print "\n$COLOR_YELLOW"."HWDETECT - MAIN ---------------$COLOR_NORMAL\n";
	GetOptions(
		'help'	   => sub { help(); exit() },
		'version'  => sub { version(); exit() },
		'debug'    => \$debug,
		'probe'    => \$probe,
		'fdisk'    => \$fdisk,
		'prefix=s' => \$prefix
	);

	# initialise
	hwdetect_init();
	hwdetect_readconf();
	
	# probe buses
	unless (defined($fdisk)) {
		hwdetect_probepci();
		hwdetect_initusb();
		hwdetect_initpcmcia();
	}
	
	# get all devices
	unless (defined($fdisk)) {
		print "\n$COLOR_YELLOW"."HWDETECT: Starting probeall $COLOR_NORMAL\n";
		hwdetect_probeall();
		hwdetect_printall();
		print "\n$COLOR_YELLOW"."HWDETECT: Starting getsata $COLOR_NORMAL\n";
		hwdetect_getsata();
		print "\n$COLOR_YELLOW"."HWDETECT: Starting getxfree $COLOR_NORMAL\n";
		hwdetect_getxfree();
		print "\n$COLOR_YELLOW"."HWDETECT: Starting getsound $COLOR_NORMAL\n";
		hwdetect_getsound();
		print "\n$COLOR_YELLOW"."HWDETECT: Starting getnetwork $COLOR_NORMAL\n";
		hwdetect_getnetwork();

		#hwdetect_getprinters();
		print "\n$COLOR_YELLOW"."HWDETECT: Starting getusb $COLOR_NORMAL\n";
		hwdetect_getusb();

		#hwdetect_getserial();
		hwdetect_writeconf();
	}
	
	# get disks
	print "\n$COLOR_YELLOW"."HWDETECT: Starting getdisks\n$COLOR_NORMAL";
	hwdetect_getdisks();
	hwdetect_writedisks();
	if (defined($fdisk)) {
		hwdetect_swapon();
		hwdetect_getmachine();
	}
	
	# deinit
	hwdetect_deinitusb() unless ($fdisk);
	hwdetect_deinit();
	print "\n$COLOR_YELLOW"."HWDETECT - DONE ---------------$COLOR_NORMAL\n";
}

#
# LiveCD hardware detection script language strings
#
# Copyright (C) 2004, Jaco Greeff <jaco@puxedo.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
#
# The latest version of this script can be found at http://livecd.berlios.de
#
# $Id: hwdetect-lang.in,v 1.8 2005/02/14 06:56:35 tom_kelly33 Exp $
#

sub getLanguages() {
	(
	   'en'    => 0,
	   'en_US' => 0,
	   'en_GB' => 0
	);
}

sub getStrings() {
	(
	'script_start'     => [ "Starting hardware detection", undef ],

	'OK'               => [ "  OK  ", undef ],
	'LOADED'           => [ "LOADED", undef ],
	'WARN'             => [ " WARN ", undef ],
	'FAILED'           => [ "FAILED", undef ],

	'arch_probe'       => [ "Detecting architecture",  undef ],
	'arch_cpus'        => [ "CPUs", undef ],
	'arch_mem_found'   => [ "memory found", undef ],
	'avail_mem'        => [ "Available Memory",  undef ],
	'avail_swap'       => [ "Available Swap",  undef ],
	
	# Available memory 128KB ***of*** 640KB 
	'of_mem'           => [ "of",  undef ],

	'pci_probe'        => [ "Probing PCI controllers",  undef ],
	'all_probe'        => [ "Probing devices",  undef ],
	'found'            => [ "found", undef ], # 10 ***found***

	'pcmcia_init'      => [ "Initialising PCMCIA controllers",  undef ],
	'pcmcia_load'      => [ "Loading base PCMCIA modules",  undef ],

	'usb_init'         => [ "Initialising USB controllers",  undef ],
	'usb_load'         => [ "Loading base USB modules",  undef ],
	'usb_mount'        => [ "Mounting USB filesystem",  undef ],
	'usb_umount'       => [ "Unmounting USB filesystem",  undef ],
	'usb_probe'        => [ "Detecting USB devices",  undef ],

	'keyboard_probe'   => [ "Selecting keyboard",  undef ],
	'mouse_probe'      => [ "Detecting mouse", undef ],
	'video_probe'      => [ "Detecting video cards", undef ],
	'monitor_probe'    => [ "Selecting monitor", undef ],
	'resolution_probe' => [ "Selecting resolution", undef ],
	'x_write'          => [ "Writing X configuration", undef ],

	'skip_sata'	   => [ "noscsi option: Skipping SATA detection", undef ],
	'sound_probe'      => [ "Detecting sound cards", undef ],
	'network_probe'    => [ "Detecting network cards", undef ],
	'printer_probe'    => [ "Detecting local printers", undef ],

	'serial_probe'     => [ "Detecting serial devices", undef ],
	# Detecting serial devices: ***(not implemented)***
	'not_impl'         => [ "(not implemented)", undef ],

	'mod_read'         => [ "Reading modules configuration", undef ],
	'mod_write'        => [ "Writing modules configuration", undef ],
	'mod_loaded'	   => [ "Module loaded", undef ],

	'disk_probe'       => [ "Detecting disk drives/partitions", undef ],
	'disk_persist'     => [ "Finding persistent partitions", undef ],
	'disk_write'       => [ "Writing partition configuration", undef ],
	'disk_create'      => [ "Creating device mountpoints", undef ],
	'disk_fstab_info'  => [ "Entries below this line were automatically added by", undef ],
	'disk_fstab_loop'  => [ "Loop image for above partition", undef ],
	'swap_activate'    => [ "Activating swap partitions", undef ]
	);
}

sub getMyLang() {
	my $language = $languages{en};
	if (defined($cmdline) && 
	    defined($cmdline{lang}) &&
	    defined($languages{$cmdline{lang}})) {
		$language = $languages{$cmdline{lang}};
	}
	$language;
}

sub getStr {
	my ($idx) = @_;
	my $str = $strings{$idx}[$lang];
	if (!defined($str)) {
		$str = $strings{$idx}[0];
		if (!defined($str)) {
			$str = "INVALID $idx";
		}
	}
	$str;
}

