#!/usr/bin/perl

$etc = "";            # debug in current dir
$etc = "/etc/";

$outfname = $etc . "modules/default";
$tmpfname = "$outfname.new";

$optfn = $etc . "modules.conf";
$noptfn = "$optfn.new";

open(DEFIN, "<$outfname") || die "no modules input file";

open(DEFOUT, ">$tmpfname");
$mymodlist = "(?: no_module ";

while(<DEFIN>) {
    /\@\@/ && last;
    chomp;
    next if ($handled{$_});
    $handled{$_}++ unless (/^#/);
    print DEFOUT $_,"\n";
}

while (<DEFIN>) {
    /\@\@/ && last;
}

print DEFOUT "#@@ Automatically generated driver list -- don't edit up to the next @@ - line\n";

$thisdrv = "<none yet specified>";

# do not use PERL globbing, since it requires CSH.
@configfiles = split(/ /,`echo -n /usr/share/hwprobe/*`);

foreach $configfile (@configfiles) {
	open(CFG, "<$configfile") || die "could not open $configfile: $!\n";
	while(<CFG>) {
	    m/^\s*#/ && next;
	    m/^\s*$/ && next;
	    $line = $_;

	    chomp $line;
	    while(substr($line, -1, 1) eq "\\" && !eof(CFG)) {
		chomp $line;
		$line .= <CFG>;
		chomp $line;
	    }
	    $line =~ s/\s+#.*$//;
	    @line = split /\s+/, $line;

	    if ($line[0] =~ m/^driver$/i) { 
		shift @line;
		$thisdrv = join(" ", @line);
	    }
	    $map{uc $line[0]} = $thisdrv;
	}
	close CFG;
}

if (open(PCI, "</proc/bus/pci/devices")) {
    while(<PCI>) {
        chomp;
        split;
        $match = $map{uc $_[1]};
        if ($match && !$handled{$match}) {
            $handled{$match}=1;
            print DEFOUT "$match\n";
        }
    }
    close PCI;
}

sub modflush {
    foreach $key (keys %modules) {
        print DEFOUT "$key\n";
#        print OPTOUT "options $key" . $modules{$key} . "\n";
        $opts .= "options $key" . $modules{$key} . "\n";
        $mymodlist .= "|$key";
    }
    undef %modules;
}

if (open(PNP, "</proc/bus/isapnp/devices"))
{
    $boardno = 1;
    while(<PNP>) {
        /^00/ && next;    # omit board IDs unless it shows we need them
        if ( ! /^$boardno/ ) {
            $boardno = substr($_, 0, 1);
            modflush();
        };
        chomp; @P = split;
        $match = $map{uc $P[2]} unless $match = $map{uc $P[1]} ;

        if ($match) {
            @match = split " ", $match;
            $modname =  $match[0];
            shift @match;
            shift @P;shift @P;shift @P;    # drop CSN/LD and Device/Compat ID

	    $pcnt=0;$icnt=0;$dcnt=0;$mcnt=0;
	    while (@P > 0) {
	    	$_ = shift @P;
		if (/^0x/) {
			next if (/^0x0+$/);
			if (length($ent)== 6) {
				$values{"m$mcnt"}=oct($_);$mcnt++;
			} else {
				$values{"p$pcnt"}=oct($_);$pcnt++;
			}
			next;
		}
		if (/^I/) {
			next if (/I0/);
                	s/^I//;
			$values{"i$icnt"}=oct("0x$_");$icnt++;
			next;
		}
		if (/^D/) {
			next if (/D4/);
                	s/^D//;
			$values{"d$dcnt"}=oct("0x$_");$dcnt++;
			next;
		}
	    }

            while (@match > 0) {
                $param = shift @match;

                # detect PnP values for "unassigned" and drop those params
                next if ($param =~ /^!/); 	# skip on config file request

                $or_val = 0;
                if ($param =~ s/\|([0-9a-fA-FXx]+)$//) {
                    $or_val = $1;
                }
		if ($param =~ /=(I[01]|D[01]|P[0-7]|M[0-3])/i) {
			next if (!defined($values{lc $1}));
			$value = $values{lc $1};
		} else {
			next;
		}

                $or_val = oct($or_val) if $or_val =~ /^0/;
                $value = 0+$value | 0+$or_val;
                $value = sprintf("0x%x", $value) if $value >= 16;

                if ($param =~ s,/, ,) {
                    ($tmodname, $param) = split(" ", $param);
                    $param =~ s/=(I[01]|D[01]|P[0-7]|M[0-3])/=$value/i;
                    # print"$tmodname $param\n";
                    $modules{$tmodname} .= " $param";
                } else {
                    $param =~ s/=.*$//; # XXX make sure this corresponds with
                    # type & num of the value above !
                    #        $modstr .= " $param=$value";

                    # print"$modname $param=$value\n";

                    $modules{$modname} .= " $param=$value";

                }
            }
        } else {
		print "No match for $_\n";
	}
    }
    close PNP;
    modflush();
}

print DEFOUT "#@@ End of automatically generated driver list\n";

while(<DEFIN>) {
    chomp;
    next if ($handled{$_});
    $handled{$_}++ unless (/^#/);
    print DEFOUT $_,"\n";
}
close(DEFIN);

close(DEFOUT);

unlink("$outfname");
link("$tmpfname", "$outfname") && unlink("$tmpfname") == 1;

$mymodlist .= ")";

open(MC, "<$optfn");
open(OPTOUT, ">$noptfn");
while(<MC>) {
    /\s*options\s+$mymodlist\s/ || print OPTOUT $_;
}
close(MC);
print OPTOUT $opts;
close(OPTOUT);
unlink("$optfn");
link("$noptfn", "$optfn") && unlink("$noptfn");
