#!/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(CFG, "<$ARGV[0]") || open (CFG,"</etc/hwprobe.config") || die "no config file: $!\n";

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>";
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;
#    print "(" . join(" ", @line) . ")\n";

    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

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

                # detect PnP values for "unassigned" and drop those params
                next if ($value =~ /I0|D4|^0x0+$/);	# unassigned: skip
                next if ($param =~ /^!/); 	# skip on config file request

                $value =~ s/^I|^D/0x/;
                $or_val = 0;
                if ($param =~ s/\|([0-9a-fA-FXx]+)$//) {
                    $or_val = $1;
                }

                $value = oct($value) if $value =~ /^0/;
                $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";
                    $modules{$modname} .= " $param=$value";

                }
            }
        }
    }
    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");
