#!/usr/bin/perl
#
# Author:  Petter Reinholdtsen
# Date:    2006-08-12
# License: GNU General Public License
#
# Merge discover device XML files together, overriding earlier files
# with information from the latter files in a list of files.
#
# Currently only able to update the model and submodel names.

use warnings;
use strict;

use XML::LibXML;

my $devices; # the current authorative data set
my %pci;

my $parser = XML::LibXML->new();

my %classmap =
    (
     'bridge'   => '0600',
     'ethernet' => '0200',
     'ide'      => '0101',
     'isdn'     => '0204',
     'modem'    => '0402',
     'scsi'     => '0100',
     'sound'    => '0401',
     'joystick' => '0904',
     'usb'      => '0c03',
     'video'    => '0400',
#     'pmc'      => '?',
     );

sub load_file {
    my $filename = shift;
    unless (defined $devices) {
	$devices = $parser->parse_file($filename);
	return;
    }
    my $names = $parser->parse_file($filename);

    my $droot = $devices->getDocumentElement;
    my $nroot = $names->getDocumentElement;

    $devices->indexElements();
    $names->indexElements();

    foreach my $nnode ($nroot->findnodes('/device_list/device')) {

	my ($vendor,$model, $subvendor, $subdevice, $name, $subname);
	for my $attr ($nnode->attributes()) {
	    $vendor = $attr->value if ("vendor" eq $attr->name);
	    $model = $attr->value if ("model" eq $attr->name);
	    $subvendor = $attr->value if ("subvendor" eq $attr->name);
	    $subdevice = $attr->value if ("subdevice" eq $attr->name);
	    $name = $attr->value if ("model_name" eq $attr->name);
	    $subname = $attr->value if ("submodel_name" eq $attr->name);
	}
	my $searchstr;
	if ($subvendor) {
	    $searchstr =
		"/device_list/device[\@vendor='$vendor' and \@model='$model' and \@subvendor='$subvendor' and \@subdevice='$subdevice']";
	} else {
	    $searchstr =
		"/device_list/device[\@vendor='$vendor' and \@model='$model' and not(\@subvendor)]";
	}
#	print STDERR "Looking for vendor=$vendor model=$model '$searchstr'\n";

	my $class;
	if (exists $pci{'card'}{"$vendor$model"}{'class'}
	    && exists $classmap{$pci{'card'}{"$vendor$model"}{'class'}}) {
	    $class = $classmap{$pci{'card'}{"$vendor$model"}{'class'}};
	    printf(STDERR "Found class %s (%s) for %s%s\n",
		   $pci{'card'}{"$vendor$model"}{'class'},
		   $class, $vendor, $model);
	}

	my $found = 0;
	my @nodes = $droot->findnodes($searchstr);
	foreach my $node (@nodes) {
	    if (!defined $subvendor && defined $class) {
		$node->setAttribute( "busclass", $class );
	    }
	    for my $attr ($node->attributes()) {
		if (defined $subvendor) {
		    if ("submodel_name" eq $attr->name &&
			$subname ne $attr->value) {
			$attr->setValue($subname);
			print STDERR "Updating submodel_name for $vendor:$model to '$subname'\n";
		    }
		} else {
		    if ("model_name" eq $attr->name &&
			$name ne $attr->value) {
			$attr->setValue($name);
			print STDERR "Updating model_name for $vendor:$model to '$name'\n";
		    }
		}
	    }
	    $found = 1;
#            print STDERR $node->toString,"\n";
	}
	unless ($found) { # Missing node, add it to devices
	    print STDERR "Adding new node ",$nnode->toString,"\n";
	    $droot->addChild($nnode);
	}
    }
}

sub load_libdetect_pci {
    my ($filename) = @_;

    my $vendorid;

    open(PCIFILE, "<$filename") || die "Unable to read $filename";
    while (<PCIFILE>) {
        chomp;

        # Remove comments
        s/^\#.+$//;

        # Skip empty lines
        next if /^\s*$/;

        if (m/^(\S{2,})\s+(.+)$/) {
            # Vendor info
            my ($id, $desc) = ($1, $2);

            $vendorid = $id;

            $pci{'vendor'}{$vendorid}{'desc'} = $desc;
        } elsif (m/^\t(\S+)\s+(.+)$/) {
            # Card info - including the vendor ID
            my ($id, $rest) = ($1, $2);

            my @f = split(/\t/, $rest);

            # Remove space at head and tail of module name
            $f[1] =~ s/^\s+(\S+)\s+$/$1/ if defined $f[1];


            # Remove or translate XFree86 server info
            if (defined $f[1] && $f[1] =~ /Card:/) {
                $f[1] = undef;
            }

            $pci{'vendor'}{$id}{'cards'}{"$id"} = $f[2];
            if ( ! exists $pci{'card'}{"$id"}{'class'} && defined $f[0] &&
		 "unknown" ne $f[0]) {
                $pci{'card'}{"$id"}{'class'}  = $f[0];
		print STDERR "Loaded class $f[0] for '$id'\n";
            }
            if ( ! exists $pci{'card'}{"$id"}{'driver'} && defined $f[1] &&
		 $f[1] ne "unknown") {
                $pci{'card'}{"$id"}{'driver'} = $f[1];
            }
            if ( ! exists $pci{'card'}{"$id"}{'desc'} && defined $f[2]) {
                $pci{'card'}{"$id"}{'desc'}   = $f[2];
            }
        }
    }
    close(PCIFILE);
}

load_libdetect_pci("../../discover1-data/trunk/pci.lst");

load_file('pci-device.xml');
load_file('pci-device-pciids.xml');
$devices->toFile("-");
