#! /usr/bin/perl use Getopt::Long; use XML::Writer; use XML::Parser; use IO; use Dumpvalue; sub help; sub read_name_file; sub read_driver_file; sub read_id_file; sub read_pcimap_file; sub read_usbmap_file; sub read_alias_file; sub read_modinfo_file; sub eisa_id; sub eisa_str; sub remove_nops; sub remove_duplicates; sub fix_driver_info; sub cmp_id; sub cmp_skey; sub cmp_item; sub match_id; sub match_skey; sub match_item; sub join_skey; sub split_item; sub get_xml_data; sub parse_xml_item; sub parse_xml_key; sub parse_xml_id; sub parse_xml_id_id; sub parse_xml_id_range; sub parse_xml_id_mask; sub parse_xml_driver; sub parse_xml_driver_display; sub parse_xml_driver_module; sub parse_xml_driver_mouse; sub parse_xml_driver_xfree; sub parse_xml_pair; sub parse_xml_cdata; sub idstr2value; sub dump2ids; sub dump2xml; sub dump_xml_item; sub dump_xml_names; sub dump_xml_drivers; sub id2xml; sub hd_dtd; sub hd_dtd_internal; $dump = new Dumpvalue(); ( $he_other, $he_bus_id, $he_baseclass_id, $he_subclass_id, $he_progif_id, $he_vendor_id, $he_device_id, $he_subvendor_id, $he_subdevice_id, $he_rev_id, $he_bus_name, $he_baseclass_name, $he_subclass_name, $he_progif_name, $he_vendor_name, $he_device_name, $he_subvendor_name, $he_subdevice_name, $he_rev_name, $he_serial, $he_driver, $he_requires, $he_nomask, $he_driver_module_insmod, $he_driver_module_modprobe, $he_driver_module_config, $he_driver_xfree, $he_driver_xfree_config, $he_driver_mouse, $he_driver_display, $he_driver_any ) = ( 0 .. 100 ); $he_class_id = $he_nomask; @ent_names = ( "other", "bus.id", "baseclass.id", "subclass.id", "progif.id", "vendor.id", "device.id", "subvendor.id", "subdevice.id", "rev.id", "bus.name", "baseclass.name", "subclass.name", "progif.name", "vendor.name", "device.name", "subvendor.name", "subdevice.name", "rev.name", "serial", "driver", "requires", "class.id", "driver.module.insmod", "driver.module.modprobe", "driver.module.config", "driver.xfree", "driver.xfree.config", "driver.mouse", "driver.display", "driver.any" ); @ent_values{@ent_names} = ( 0 .. 100 ); @xml_names = ( "other", "bus", "baseclass", "subclass", "progif", "vendor", "device", "subvendor", "subdevice", "revision", "bus", "baseclass", "subclass", "progif", "vendor", "device", "subvendor", "subdevice", "revision", "serial", "driver", "requires" ); @xml_values{@xml_names} = ( 0 .. 100 ); ( $tag_none, $tag_pci, $tag_eisa, $tag_usb, $tag_special, $tag_pcmcia ) = ( 0 .. 5 ); @tag_name = ( "", "pci", "eisa", "usb", "special", "pcmcia" ); @tag_values{@tag_name} = ( 0 .. 5 ); $tag_values{none} = 0; ( $flag_id, $flag_range, $flag_mask, $flag_string, $flag_regexp ) = ( 0 .. 4 ); $flag_cont = 8; # map usb modules to device classes %usbmod2class = ( 'ov511' => [ 0x10f, 0 ], 'pwc' => [ 0x10f, 0 ], 'hpusbscsi' => [ 0x10c, 0 ], 'microtek' => [ 0x10c, 0 ], 'scanner' => [ 0x10c, 0 ] ); # options $opt_write_ids = 1; $opt_write_xml = 0; $opt_sort_ids = 0; $opt_sort_reverse = 0; $opt_sort_random = 0; # for testing $opt_split = 0; $opt_with_source = 0; $opt_fix_driver = 1; $opt_help = 0; $opt_internal_dtd = 0; $opt_ok = GetOptions( 'ids' => \$opt_write_ids, 'no-ids' => sub { $opt_write_ids = 0 }, 'xml' => \$opt_write_xml, 'no-xml' => sub { $opt_write_xml = 0 }, 'sort' => \$opt_sort, 'reverse' => \$opt_sort_reverse, 'random' => \$opt_sort_random, 'split' => \$opt_split, 'with-source' => \$opt_with_source, 'fix-driver' => \$opt_fix_driver, 'no-fix-driver' => sub { $opt_fix_driver = 0 }, 'internal-dtd' => \$opt_internal_dtd, 'help' => \&help ) ; for $f (@ARGV) { if(open F, $f) { @f = (); close F; # file format check undef $format; for (@f) { if(/^\s*\<\?xml\s/) { $format = 'xml'; last; } if(/^#\s+pci\s+module\s+vendor\s+device\s+subvendor\s+subdevice\s+class\s+class_mask\s+driver_data\s*$/) { $format = 'pcimap'; last; } if(/^#\s+usb\s+module\s+match_flags\s+idVendor\s+idProduct\s+/) { $format = 'usbmap'; last; } if(/^\s*alias\s+(pci|pnp|usb):\S+\s+\S+$/) { $format = 'alias'; last; } if(/^\s*alias:\s+(pci|pnp|usb):\S+\s*$/) { $format = 'modinfo'; last; } } if(!$format) { $i = join "|", map "\Q$_", @ent_names; for (@f) { if(/^\s*[+&|]?($i)\s/) { $format = 'ids'; last; } } } if(!$format) { for (@f) { if(/^\t[a-z]\s/) { $format = 'drivers'; last; } } } $format = 'names' if !$format; if($format eq 'names') { print STDERR "====== \"$f\": name info ======\n"; read_name_file $f, \@f; } elsif($format eq 'drivers') { print STDERR "====== \"$f\": driver info ======\n"; read_driver_file $f, \@f; } elsif($format eq 'xml') { print STDERR "====== \"$f\": xml info ======\n"; $xmlp = new XML::Parser(Style => 'Tree', ParseParamEnt => 1); get_xml_data $xmlp->parsefile($f); } elsif($format eq 'ids') { print STDERR "====== \"$f\": id info ======\n"; read_id_file $f, \@f; } elsif($format eq 'pcimap') { print STDERR "====== \"$f\": pcimap info ======\n"; read_pcimap_file $f, \@f; } elsif($format eq 'usbmap') { print STDERR "====== \"$f\": usbmap info ======\n"; read_usbmap_file $f, \@f; } elsif($format eq 'alias') { print STDERR "====== \"$f\": alias info ======\n"; read_alias_file $f, \@f; } elsif($format eq 'modinfo') { print STDERR "====== \"$f\": module info ======\n"; read_modinfo_file $f, \@f; } } else { die "$f: $!\n" } } print STDERR "removing unnecessary items\n"; remove_nops; print STDERR "got ${\scalar @hd} items\n"; if($opt_fix_driver) { fix_driver_info; } if($opt_split) { print STDERR "splitting items\n"; for (@hd) { push @hd_new, split_item($_); } @hd = @hd_new; undef @hd_new; } if($opt_sort_ids) { print STDERR "sorting\n"; if($opt_sort_random) { @hd = sort { $cmp_item_cnt++, rand() <=> rand() } @hd; } elsif($opt_sort_reverse) { @hd = sort { cmp_item $b, $a } @hd; } else { @hd = sort { cmp_item $a, $b } @hd; } } if($opt_write_ids) { print STDERR "writing \"hd.ids\"\n"; dump2ids; } if($opt_write_xml) { print STDERR "writing \"hd.xml\"\n"; dump2xml; } print STDERR "cmps: $cmp_item_cnt\n" if $cmp_item_cnt; # $dump->dumpValue( \@hd ); # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub help { print STDERR "Usage: convert_hd [options] files\n" . "Convert various hardware info to libhd/hwinfo internal format or to XML.\n" . " --ids write internal format (default) to \"hd.ids\"\n" . " --no-ids do not write internal format\n" . " --xml write XML to \"hd.xml\", DTD to \"hd.dtd\"\n" . " --no-xml do not write XML (default)\n" . " --with-source add comment to each item indicating info source\n" . " --internal-dtd generate internal dtd\n\n" . " Note: for more sophisticated operations on hardware data use check_hd.\n"; exit 0; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub num { return $_[0] =~ /^0/ ? oct $_[0] : return $_[0] + 0; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # read file with name/class info # # (either pciutils or SaX/SaX2 format) # sub read_name_file { my ( $file_name, $file, $line, $sax_version, $tag, $id, $val, $ent ); my ( @id0, @id1, @id2, @id3, @id4, $raw, $opt, $ext, $srv, $str ); local $_; my $rnf_add_id0 = sub { my ( $id0, $name0, $ent_id0, $ent_name0, $id, $val ); # note: $tag belongs to read_name_file() ( $ent_id0, $ent_name0, $tag, $id0, $name0 ) = @_; $ent = $ent_id0; @id0 = ( $flag_id, $tag, $id0 ); undef @id1; undef @id2; undef @id3; $id->[$ent_id0] = [ @id0 ]; $val->[$ent_name0] = [ $flag_string, $name0 ]; push @hd, [ "$file_name($line)", [ $id ], $val ]; }; my $rnf_add_bus = sub { $rnf_add_id0->($he_bus_id, $he_bus_name, 0, @_); }; my $rnf_add_baseclass = sub { $rnf_add_id0->($he_baseclass_id, $he_baseclass_name, 0, @_); }; my $rnf_add_vendor = sub { $rnf_add_id0->($he_vendor_id, $he_vendor_name, @_); }; my $rnf_add_subdevice = sub { my ( $id2, $id3, $range, $name, $class, $id, $val ); ( $id2, $id3, $range, $name, $class ) = @_; @id2 = ( $flag_id, $tag, $id2 ); @id3 = ( $flag_id, $tag, $id3 ); $id3[3] = $range if defined $range; if($ent == $he_device_id || $ent == $he_subdevice_id) { $ent = $he_subdevice_id; $id->[$he_vendor_id] = [ @id0 ]; $id->[$he_device_id] = [ @id1 ]; $id->[$he_subvendor_id] = [ @id2 ]; $id->[$he_subdevice_id] = [ @id3 ]; $val->[$he_subdevice_name] = [ $flag_string, $name ]; if(defined $class) { $val->[$he_baseclass_id] = [ $flag_id, $tag_none, $class >> 8 ]; $val->[$he_subclass_id] = [ $flag_id, $tag_none, $class & 0xff ]; } } else { die "oops $file_name($line): subdevice id expected\n"; } push @hd, [ "$file_name($line)", [ $id ], $val ]; }; ( $file_name, $file ) = @_; $line = 0; undef $sax_version; for (@$file) { $line++; chomp; s/\s*$//; next if /^\s*[#;]/; next if /^$/; # SaX Identity file if(/^NAME=(.+?)§DEVICE=(.+?)§VID=0x([0-9a-fA-F]+?)§DID=0x([0-9a-fA-F]+?)§SERVER=([^§]+)(§EXT=([^§]*))?(§OPT=([^§]*))?(§RAW=([^§]*))?$/) { # 1 2 3 4 5 6 7 8 9 10 11 $rnf_add_vendor->($tag_pci, hex($3), $1); @id0 = ( $flag_id, $tag, hex($3) ); @id1 = ( $flag_id, $tag, hex($4) ); @id3 = ( $flag_string, $2 ); $id = []; $val = []; $id->[$he_vendor_id] = [ @id0 ]; $id->[$he_device_id] = [ @id1 ]; $val->[$he_device_name] = [ @id3 ]; push @hd, [ "$file_name($line)", [ $id ], $val ]; ( $srv, $ext, $opt, $raw ) = ( $5, $7, $9, $11 ); $sax_tmp = $srv =~ /^3DLABS|MACH64|P9000|RUSH|S3|SVGA|TGA$/ ? 1 : 2; $sax_version = $sax_tmp unless defined $sax_version; die "line has SaX$sax_tmp format (expected SaX$sax_version): $file_name($line)\n" if $sax_tmp != $sax_version; $id = []; $val = []; $id->[$he_vendor_id] = [ @id0 ]; $id->[$he_device_id] = [ @id1 ]; if($opt) { $str = join "|", ( $sax_version == 1 ? 3 : 4, $srv, undef, undef, $ext, $opt ); } elsif($ext) { $str = join "|", ( $sax_version == 1 ? 3 : 4, $srv, undef, undef, $ext ); } else { $str = join "|", ( $sax_version == 1 ? 3 : 4, $srv ); } @id4 = ( "x\t$str" ); if($raw) { for $str (split /,/, $raw) { $id4[0] .= "\x00X\t$str" } } $val->[$he_driver] = [ $flag_string, @id4 ]; push @hd, [ "$file_name($line)", [ $id ], $val ]; } elsif(/^B\s+([0-9a-fA-F]+)\s+(.*?)\s*$/) { $rnf_add_bus->(hex($1), $2); } elsif(/^C\s+([0-9a-fA-F]+)\s+(.*?)\s*$/) { $rnf_add_baseclass->(hex($1), $2); } elsif(/^([0-9a-fA-F]{4})(\s+(.*?))?\s*$/) { $rnf_add_vendor->($tag_pci, hex($1), $3); } elsif(/^u([0-9a-fA-F]{4})(\s+(.*?))?\s*$/) { $rnf_add_vendor->($tag_usb, hex($1), $3); } elsif(/^s([0-9a-fA-F]{4})(\s+(.*?))?\s*$/) { $rnf_add_vendor->($tag_special, hex($1), $3); } elsif(/^([A-Z_@]{3})(\s+(.*?))?\s*$/) { $rnf_add_vendor->($tag_eisa, eisa_id($1), $3); } elsif(/^\t([0-9a-fA-F]{1,4})(\+([0-9a-fA-F]+))?(\.([0-9a-fA-F]+))?(\s+(.*?))?\s*$/) { $range = $3 ? hex($3) : undef; $class = $5 ? hex($5) : undef; @id1 = ( $flag_id, $tag, hex($1) ); $id1[3] = $range if defined $range; undef @id2; undef @id3; $id = []; $val = []; if($ent == $he_baseclass_id || $ent == $he_subclass_id) { $ent = $he_subclass_id; $id->[$he_baseclass_id] = [ @id0 ]; $id->[$he_subclass_id] = [ @id1 ]; $val->[$he_subclass_name] = [ $flag_string, $7 ]; } elsif($ent == $he_vendor_id || $ent == $he_device_id || $ent == $he_subdevice_id) { $ent = $he_device_id; $id->[$he_vendor_id] = [ @id0 ]; $id->[$he_device_id] = [ @id1 ]; $val->[$he_device_name] = [ $flag_string, $7 ]; if(defined $class) { $val->[$he_baseclass_id] = [ $flag_id, $tag_none, $class >> 8 ]; $val->[$he_subclass_id] = [ $flag_id, $tag_none, $class & 0xff ]; } } else { die "oops $file_name($line): device id expected\n"; } push @hd, [ "$file_name($line)", [ $id ], $val ]; } elsif($ent == $he_subclass_id && /^\t\t([0-9a-fA-F]+)\s+(.*?)\s*$/) { @id2 = ( $flag_id, $tag, hex($1) ); undef @id3; $id = []; $val = []; $id->[$he_baseclass_id] = [ @id0 ]; $id->[$he_subclass_id] = [ @id1 ]; $id->[$he_progif_id] = [ @id2 ]; $val->[$he_progif_name] = [ $flag_string, $2 ]; push @hd, [ "$file_name($line)", [ $id ], $val ]; } elsif(/^\t\t([0-9a-fA-F]{4})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?(\.([0-9a-fA-F]+))?(\s+(.*?))?\s*$/) { $rnf_add_subdevice->(hex($1), hex($2), $4 ? hex($4) : undef, $8, $6 ? hex($6) : undef); } elsif(/^\t\t([A-Z_@]{3})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?(\.([0-9a-fA-F]+))?(\s+(.*?))?\s*$/) { $rnf_add_subdevice->(eisa_id($1), hex($2), $4 ? hex($4) : undef, $8, $6 ? hex($6) : undef); } elsif(/^\t\t([0-9a-fA-F]{4})([0-9a-fA-F]{4})\s+(.*?)\s*$/) { # NOTE: subvendor & subdevice ids are reversed! $rnf_add_subdevice->(hex($2), hex($1), undef, $3); } else { die "invalid line: $file_name($line)\n"; } } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # read file with driver info # sub read_driver_file { my ( $line, @drv, $file, $file_name, $drv_type, $tag ); local $_; my $rdf_save_drv = sub { if($drv_type) { push @hd, [ @drv ] if defined @drv; @drv = ( "$file_name($line)" ); $drv[2][$he_driver] = [ $flag_string ]; $drv_type = undef; } }; my $rdf_add_id = sub { my ( $tag, $id0, $id1, $range1, $id2, $id3, $range3, $id ); ( $tag, $id0, $id1, $range1, $id2, $id3, $range3 ) = @_; $rdf_save_drv->(); $id = []; @id0 = ( $flag_id, $tag, $id0 ); @id1 = ( $flag_id, $tag, $id1 ); $id1[3] = $range1 if defined $range1; $id->[$he_vendor_id] = [ @id0 ]; $id->[$he_device_id] = [ @id1 ]; if(defined $id2) { @id2 = ( $flag_id, $tag, $id2 ); @id3 = ( $flag_id, $tag, $id3 ); $id3[3] = $range3 if defined $range3; $id->[$he_subvendor_id] = [ @id2 ]; $id->[$he_subdevice_id] = [ @id3 ]; } push @{$drv[1]}, $id; }; ( $file_name, $file ) = @_; $drv_type = 1; for (@$file) { $line++; chomp; s/\s*$//; next if /^[#;]/; next if /^$/; if(/^([us]?)([0-9a-fA-F]{4})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s*$/) { $tag = $tag_pci; $tag = $tag_usb if $1 eq 'u'; $tag = $tag_special if $1 eq 's'; $rdf_add_id->($tag, hex($2), hex($3), $5 ? hex($5) : undef); } elsif(/^([A-Z_@]{3})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s*$/) { $rdf_add_id->($tag_eisa, eisa_id($1), hex($2), $4 ? hex($4) : undef); } elsif(/^([us]?)([0-9a-fA-F]{4})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s+([us]?)([0-9a-fA-F]{4})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s*$/) { $tag = $tag_pci; $tag = $tag_usb if $1 eq 'u'; $tag = $tag_special if $1 eq 's'; $rdf_add_id->($tag, hex($2), hex($3), $5 ? hex($5) : undef, hex($7), hex($8), $10 ? hex($10) : undef); } elsif(/^([A-Z_@]{3})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s+([A-Z_@]{3})\s+([0-9a-fA-F]{4})(\+([0-9a-fA-F]+))?\s*$/) { $rdf_add_id->($tag_eisa, eisa_id($1), hex($2), $4 ? hex($4) : undef, eisa_id($5), hex($6), $8 ? hex($8) : undef); } elsif(/^\t([a-z])\s+(.*?)\s*$/) { push @{$drv[2][$he_driver]}, "$1\t$2"; $drv_type = $1; } elsif($drv_type && /^\t\t\s*(.*)$/) { $drv_type = "X" if $drv_type eq "x"; $drv_type = "M" if $drv_type eq "m"; $drv[2][$he_driver][-1] .= "\x00$drv_type\t$1"; } else { die "invalid line: $file_name($line)\n"; } } $rdf_save_drv->(); } sub num { return $_[0] =~ /^0/ ? oct $_[0] : return $_[0] + 0; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # read file with id info # sub read_id_file { my ( $line, $file, $file_name, $tag, $pre, $fields, @item, @id, $state, $keyid ); my ( $is_id, $i ); local $_; my $rif_save_item = sub { if(@item > 1) { push @hd, [ @item ]; } @item = ( "$file_name($line)" ); }; # parse id field my $str2id = sub { my ($val, $id, $tag, $mask, $range, @id); $val = shift; if($val =~ s/^(${\join '|', @tag_name})\s+//o) { die "internal oops: $file_name($line)\n" unless exists $tag_values{$1}; $tag = $tag_values{$1}; } else { $tag = 0; } if($val =~ /^\s*(\S+)\s*([&+])\s*(\S+)\s*$/) { $id = $1; if($2 eq "+") { $range = $3; } else { $mask = $3; } } else { $id = $val; } if(defined $range) { if($range =~ /^(0x[0-9a-zA-Z]+|\d+)$/) { $range = num $range; } else { die "$file_name($line): invalid range\n" } } if(defined $mask) { if($mask =~ /^(0x[0-9a-zA-Z]+|\d+)$/) { $mask = num $mask; } else { die "$file_name($line): invalid mask\n" } } if($id =~ /^(0x[0-9a-zA-Z]+|\d+)$/) { $id = num $id; } elsif(($tag == $tag_none || $tag == $tag_eisa) && $id =~ /^[A-Z_@]{3}$/) { $id = eisa_id $id; $tag = $tag_eisa; } else { die "$file_name($line): invalid id\n" } @id = ( $flag_id, $tag, $id ); $id[3] = $range if defined $range; $id[4] = $mask if defined $mask; return \@id; }; ( $file_name, $file ) = @_; $fields = join "|", map "\Q$_", @ent_names; $state = 0; $rif_save_item->(); for (@$file) { $line++; chomp; s/\s*$//; next if /^\s*[#;]/; next if /^$/; if(/^\s*([+&|]?)($fields)\s+(.+)/) { ($pre, $key, $val) = ($1, $2, $3); # print ">$pre< $is_id>$key< >$val<\n"; die "internal oops: $file_name($line)\n" unless exists $ent_values{$key}; $keyid = $ent_values{$key}; $is_id = $keyid < $he_nomask && $key =~ /\.id$/ ? 1 : 0; } else { die "invalid line: $file_name($line)\n"; } if($pre eq "") { die "invalid line: $file_name($line)\n" unless $state == 0 || $state == 2; if($state == 2) { $item[2] = [ @id ]; undef @id; } $rif_save_item->(); $state = 1; } elsif($pre eq "|") { die "invalid line: $file_name($line)\n" unless $state == 1; push @{$item[1]}, [ @id ]; undef @id; } elsif($pre eq "&") { die "invalid line: $file_name($line)\n" unless $state == 1; } elsif($pre eq "+") { die "invalid line: $file_name($line)\n" unless $state == 1 || $state == 2; if($state == 1) { push @{$item[1]}, [ @id ]; undef @id; } $state = 2; } else { die "internal oops: $file_name($line)\n"; } if($is_id) { $id[$keyid] = $str2id->($val); } elsif($keyid < $he_nomask) { $id[$keyid] = [ $flag_string, $val ]; } elsif($keyid == $he_class_id) { $i = ${$str2id->($val)}[2]; $id[$he_baseclass_id] = [ $flag_id, $tag_none, $i >> 8 ]; $id[$he_subclass_id] = [ $flag_id, $tag_none, $i & 0xff ]; } else { undef $i; if($keyid == $he_driver_module_insmod) { $i = "i"; } elsif($keyid == $he_driver_module_modprobe) { $i = "m"; } elsif($keyid == $he_driver_module_config) { $i = "M"; } elsif($keyid == $he_driver_xfree) { $i = "x"; } elsif($keyid == $he_driver_xfree_config) { $i = "X"; } elsif($keyid == $he_driver_mouse) { $i = "p"; } elsif($keyid == $he_driver_display) { $i = "d"; } elsif($keyid == $he_driver_any) { $i = "a"; } else { die "unhandled entry: $file_name($line)\n" } $val = "$i\t$val"; if(!defined $id[$he_driver]) { $id[$he_driver] = [ $flag_string ]; } if($i eq "X" || $i eq "M") { $id[$he_driver]->[-1] .= "\x00$val" } else { push @{$id[$he_driver]}, $val; } } } if($state == 2) { $item[2] = [ @id ]; undef @id; } $rif_save_item->(); } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # read pcimap file # sub read_pcimap_file { my (@l, $id, $n, $key, $val, $mask); local $_; ( $file_name, $file ) = @_; for (@$file) { $line++; chomp; s/\s*$//; next if /^\s*#/; next if /^$/; @l = split; die "invalid line: $file_name($line)\n" unless @l == 8; $val = []; $val->[$he_driver] = [ $flag_string, "m\t$l[0]" ]; $key = []; $key->[$he_vendor_id] = [ $flag_id, $tag_pci, $n ] if ($n = num $l[1]) != 0xffffffff; $key->[$he_device_id] = [ $flag_id, $tag_pci, $n ] if ($n = num $l[2]) != 0xffffffff; $key->[$he_subvendor_id] = [ $flag_id, $tag_pci, $n ] if ($n = num $l[3]) != 0xffffffff; $key->[$he_subdevice_id] = [ $flag_id, $tag_pci, $n ] if ($n = num $l[4]) != 0xffffffff; $n = num $l[6]; if($mask = ($n >> 16) & 0xff) { $key->[$he_baseclass_id] = [ $flag_id, $tag_none, (num($l[5]) >> 16) & 0xff ]; if($mask != 0xff) { $key->[$he_baseclass_id][4] = (~$mask & 0xff); } } if($mask = ($n >> 8) & 0xff) { $key->[$he_subclass_id] = [ $flag_id, $tag_none, (num($l[5]) >> 8) & 0xff ]; if($mask != 0xff) { $key->[$he_subclass_id][4] = (~$mask & 0xff); } } if($mask = $n & 0xff) { $key->[$he_progif_id] = [ $flag_id, $tag_none, num($l[5]) & 0xff ]; if($mask != 0xff) { $key->[$he_progif_id][4] = (~$mask & 0xff); } } push @hd, [ "$file_name($line)", [ $key ], $val ]; } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # read usbmap file # sub read_usbmap_file { my (@l, $id, $n, $key, $val, $mask); local $_; ( $file_name, $file ) = @_; for (@$file) { $line++; chomp; s/\s*$//; next if /^\s*#/; next if /^$/; @l = split; die "invalid line: $file_name($line)\n" unless @l == 13; next if num($l[1]) != 3; # match_flags != 3 $val = []; $key = []; $key->[$he_vendor_id] = [ $flag_id, $tag_usb, num($l[2]) ]; $key->[$he_device_id] = [ $flag_id, $tag_usb, num($l[3]) ]; $val->[$he_driver] = [ $flag_string, "m\t$l[0]" ]; if($usbmod2class{$l[0]}) { $val->[$he_baseclass_id] = [ $flag_id, $tag_none, $usbmod2class{$l[0]}[0] ] if defined $usbmod2class{$l[0]}[0]; $val->[$he_subclass_id] = [ $flag_id, $tag_none, $usbmod2class{$l[0]}[1] ] if defined $usbmod2class{$l[0]}[1]; } push @hd, [ "$file_name($line)", [ $key ], $val ]; } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # read alias file # sub read_alias_file { my ($f, $id, $n, $key, $val, $mask, $tag, $module, $spec, $t1, $t2); local $_; $f = '[0-9A-F*]+'; ( $file_name, $file ) = @_; for (@$file) { $line++; chomp; s/\s*$//; next if /^\s*#/; next if /^$/; next unless /^\s*alias\s+(pci|pnp|usb):(\S+)\s+(\S+)/; $tag = $tag_pci if $1 eq 'pci'; $tag = $tag_eisa if $1 eq 'pnp'; $tag = $tag_usb if $1 eq 'usb'; $spec = $2; $module = $3; $val = []; $val->[$he_driver] = [ $flag_string, "m\t$module" ]; $key = []; if($spec =~ /^v($f)d($f)sv($f)sd($f)bc($f)sc($f)i($f)$/ ) { $key->[$he_vendor_id] = [ $flag_id, $tag, hex $1 ] if $1 ne '*'; $key->[$he_device_id] = [ $flag_id, $tag, hex $2 ] if $2 ne '*'; $key->[$he_subvendor_id] = [ $flag_id, $tag, hex $3 ] if $3 ne '*'; $key->[$he_subdevice_id] = [ $flag_id, $tag, hex $4 ] if $4 ne '*'; $key->[$he_baseclass_id] = [ $flag_id, $tag_none, hex $5 ] if $5 ne '*'; $key->[$he_subclass_id] = [ $flag_id, $tag_none, hex $6 ] if $6 ne '*'; $key->[$he_progif_id] = [ $flag_id, $tag_none, hex $7 ] if $7 ne '*'; push @hd, [ "$file_name($line)", [ $key ], $val ]; } elsif($spec =~ /^v($f)p($f)dl($f)dh($f)dc($f)dsc($f)dp($f)ic($f)isc($f)ip($f)$/ ) { if( $3 == '*' && $4 == '*' && $5 == '*' && $6 == '*' && $7 == '*' && $8 == '*' && $9 == '*' && $10 == '*' ) { $key->[$he_vendor_id] = [ $flag_id, $tag, hex $1 ] if $1 ne '*'; $key->[$he_device_id] = [ $flag_id, $tag, hex $2 ] if $2 ne '*'; push @hd, [ "$file_name($line)", [ $key ], $val ]; } } elsif($spec =~ /^[c|d](\S{3})([0-9a-fA-FX]{4})/ ) { $t1 = $1; $t2 = $2; if($t1 =~ /[\@A-Z\[\\\]\^_]{3}/ && $t2 ne 'XXXX') { $key->[$he_vendor_id] = [ $flag_id, $tag, eisa_id $t1 ]; $key->[$he_device_id] = [ $flag_id, $tag, hex $t2 ]; push @hd, [ "$file_name($line)", [ $key ], $val ]; } } else { die "invalid line: $file_name($line)\n" } } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # read modinfo data # sub read_modinfo_file { my ($f, $id, $n, $key, $val, $mask, $tag, $module, $spec, $t1, $t2); local $_; $f = '[0-9A-F*]+'; ( $file_name, $file ) = @_; for (@$file) { $line++; chomp; s/\s*$//; next if /^\s*#/; next if /^$/; if(m#([^/]+)\.ko:$#) { $module = $1; next; } next unless /^\s*alias:\s+(pci|pnp|usb):(\S+)\s*$/; $tag = $tag_pci if $1 eq 'pci'; $tag = $tag_eisa if $1 eq 'pnp'; $tag = $tag_usb if $1 eq 'usb'; $spec = $2; $val = []; $val->[$he_driver] = [ $flag_string, "m\t$module" ]; $key = []; if($spec =~ /^v($f)d($f)sv($f)sd($f)bc($f)sc($f)i($f)$/ ) { $key->[$he_vendor_id] = [ $flag_id, $tag, hex $1 ] if $1 ne '*'; $key->[$he_device_id] = [ $flag_id, $tag, hex $2 ] if $2 ne '*'; $key->[$he_subvendor_id] = [ $flag_id, $tag, hex $3 ] if $3 ne '*'; $key->[$he_subdevice_id] = [ $flag_id, $tag, hex $4 ] if $4 ne '*'; $key->[$he_baseclass_id] = [ $flag_id, $tag_none, hex $5 ] if $5 ne '*'; $key->[$he_subclass_id] = [ $flag_id, $tag_none, hex $6 ] if $6 ne '*'; $key->[$he_progif_id] = [ $flag_id, $tag_none, hex $7 ] if $7 ne '*'; push @hd, [ "$file_name($line)", [ $key ], $val ]; } elsif($spec =~ /^v($f)p($f)dl($f)dh($f)dc($f)dsc($f)dp($f)ic($f)isc($f)ip($f)$/ ) { if( $3 == '*' && $4 == '*' && $5 == '*' && $6 == '*' && $7 == '*' && $8 == '*' && $9 == '*' && $10 == '*' ) { $key->[$he_vendor_id] = [ $flag_id, $tag, hex $1 ] if $1 ne '*'; $key->[$he_device_id] = [ $flag_id, $tag, hex $2 ] if $2 ne '*'; push @hd, [ "$file_name($line)", [ $key ], $val ]; } } elsif($spec =~ /^[c|d](\S{3})([0-9a-fA-FX]{4})/ ) { $t1 = $1; $t2 = $2; if($t1 =~ /[\@A-Z\[\\\]\^_]{3}/ && $t2 ne 'XXXX') { $key->[$he_vendor_id] = [ $flag_id, $tag, eisa_id $t1 ]; $key->[$he_device_id] = [ $flag_id, $tag, hex $t2 ]; push @hd, [ "$file_name($line)", [ $key ], $val ]; } } else { die "invalid line: $file_name($line)\n" } } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # convert 3-letter eisa id to number # sub eisa_id { my ( $str, $id, $i, $j ); $str = shift; $id = 0; die "internal oops" unless length($str) == 3; for($i = 0; $i < 3; $i++) { $id <<= 5; $j = ord substr $str, $i, 1; $j -= ord('A') - 1; die "internal oops" unless $j >= 0 && $j <= 0x1f; $id += $j; } return $id; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # convert numerical eisa id to 3-letter string # sub eisa_str { my ( $id, $str ); $id = shift; die "internal oops: eisa id \"$id\"" unless $id >= 0 && $id <= 0x7fff; $str = chr((($id >> 10) & 0x1f) + ord('A') - 1); $str .= chr((($id >> 5) & 0x1f) + ord('A') - 1); $str .= chr(( $id & 0x1f) + ord('A') - 1); return $str; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # remove entries that have no effect # sub remove_nops { my ($hd, $id, $f, $i, $cf); local $_; for $hd (@hd) { if(!defined($hd->[1]) || !@{$hd->[1]} || !defined($hd->[2]) || !@{$hd->[2]}) { undef $hd; next; } for $id (@{$hd->[1]}, $hd->[2]) { if(defined($id)) { $cf = 0; for $f (@$id) { if(defined $f) { $cf++; if(@$f == 2 && $f->[0] == $flag_string && $f->[1] eq "") { undef $f; $cf--; } } } undef $id if !$cf; } } if(!defined($hd->[1]) || !@{$hd->[1]} || !defined($hd->[2]) || !@{$hd->[2]}) { print STDERR "$hd->[0] has no info, dropped\n"; undef $hd; next; } } @hd = grep { defined } @hd; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # remove duplicate entries # sub remove_duplicates { my ($hd, $hd0, $hd1, $len, $i, $j, $m, $v, $buf, $errors, $drop); local $_; $len = @hd; for($j = 0; $j < $len; $j++) { print STDERR ">> $j\r"; $hd0 = \$hd[$j]; for($i = $j + 1; $i < $len; $i++) { $hd1 = \$hd[$i]; $m = match_item $$hd0, $$hd1; # print "$$hd0->[0] -- $$hd1->[0]: $m\n"; if($m) { $drop = cmp_item $$hd0, $$hd1; $drop = !$drop || abs($drop) == 2 ? ", dropped" : undef; undef $buf; # print STDERR "j: $$hd0->[0], $$hd1->[0]\n"; $v = join_skey $$hd0->[2], $$hd1->[2], \$buf, \$errors; if($errors) { print STDERR "$$hd1->[0] conflicts with $$hd0->[0]$drop:\n$buf\n"; $$hd1 = undef if $drop; } else { if($drop) { print STDERR "$$hd1->[0] added to $$hd0->[0] and dropped\n"; $$hd0->[2] = $v; # $$hd1 = undef; } else { print STDERR "$$hd1->[0] shadowed by $$hd0->[0]\n"; $$hd0->[2] = $v; } } } } } @hd = grep { defined } @hd; for $hd (@hd) { if( !defined($hd->[2]) || !defined($hd->[2][$he_driver]) || !(defined($hd->[2][$he_device_name]) || defined($hd->[2][$he_subdevice_name])) ) { undef $hd; next; } } @hd = grep { defined } @hd; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # remove duplicate entries # sub remove_duplicatesx { my ($hd0, $hd1, $len, $i, $j, $m, $v, $buf, $errors, $drop); local $_; $len = @hd; for($j = 0; $j < $len; $j++) { print STDERR ">> $j\r"; $hd0 = \$hd[$j]; for($i = $j + 1; $i < $len; $i++) { $hd1 = \$hd[$i]; $m = match_item $$hd0, $$hd1; # print "$$hd0->[0] -- $$hd1->[0]: $m\n"; if($m) { $drop = cmp_item $$hd0, $$hd1; $drop = !$drop || abs($drop) == 2 ? ", dropped" : undef; undef $buf; $v = join_skey $$hd0->[2], $$hd1->[2], \$buf, \$errors; if($errors) { print STDERR "$$hd1->[0] conflicts with $$hd0->[0]$drop:\n$buf\n"; $$hd1 = undef if $drop; } else { if($drop) { print STDERR "$$hd1->[0] added to $$hd0->[0] and dropped\n"; $$hd0->[2] = $v; $$hd1 = undef; } else { print STDERR "$$hd1->[0] shadowed by $$hd0->[0]\n"; } } } } } @hd = grep { defined } @hd; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # fix drive info # sub fix_driver_info { my ($hd, $hid, $drv, $i, @i, @info, @req, %req); for $hd (@hd) { if( !defined($hd->[2]) || !defined($hd->[2][$he_driver]) ) { next; } $hid = $hd->[2][$he_driver]; next unless $hid->[0] == $flag_string; undef @req; for $drv (@$hid[1 .. @$hid - 1]) { @i = split /\x00/, $drv; for $i (@i) { next if $i =~ /^[MX]\t/; $i =~ s/\|+$//; next unless $i =~ /^x\t/; @info = split /\|/, $i; # remove leasding 'XF86_' from server name $info[1] =~ s/^XF86_// if $info[1]; # sort package, extension and option lists push @req, split /,/, $info[3] if $info[3]; # $info[3] = join ',', sort split /,/, $info[3] if $info[3]; $info[3] = undef if $info[3]; $info[4] = join ',', sort split /,/, $info[4] if $info[4]; $info[5] = join ',', sort split /,/, $info[5] if $info[5]; $info[6] = join ',', sort { $a <=> $b } split /,/, $info[6] if $info[6]; $i = join '|', @info; } $drv = join "\x00", @i; # print ">$drv<\n" } if(@req) { $hid = $hd->[2][$he_requires]; if($hid) { if($hid->[0] != $flag_string) { die "oops, invalid data" } push @req, split /\|/, $hid->[1]; $hid->[1] = join '|', @req; } else { $hd->[2][$he_requires] = [ $flag_string, join('|', @req) ]; } } } for $hd (@hd) { if( !defined($hd->[2]) || !defined($hd->[2][$he_requires]) ) { next; } $hid = $hd->[2][$he_requires]; next unless $hid->[0] == $flag_string; undef @req; undef %req; @req = split /\|/, $hid->[1]; @req{@req} = @req; $hid->[1] = join '|', sort keys %req; } } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # hd: [ "source", [ skey, skey, ... ], [ val ] ] # skey/val: [ ... , id, ..., id, ... ] # id: [ $flag_id, $tag, $value, $range, $mask ] # id: [ $flag_string, "str", "str", ... ] sub cmp_id { my ($id0, $id1, $len0, $len1, $len, $i, $k); ($id0, $id1) = @_; return 0 if !defined($id0) && !defined($id1); return -1 if !defined($id0); return 1 if !defined($id1); if($id0->[0] != $id1->[0]) { return $id0->[0] <=> $id1->[0]; } $len0 = @$id0; $len1 = @$id1; $len = $len0 < $len1 ? $len0 : $len1; if($id0->[0] == $flag_string) { for($i = 1; $i < $len; $i++) { $k = $id0->[$i] cmp $id1->[$i]; return $k if $k; } return $len0 <=> $len1; } if($id0->[0] == $flag_id) { $k = $id0->[1] <=> $id1->[1]; return $k if $k; $k = $id0->[2] <=> $id1->[2]; return $k if $k; $k = $len0 <=> $len1; return $k if $k || $len <= 3; # print "-\n"; # $dump->dumpValue( $id0 ); # $dump->dumpValue( $id1 ); # die "internal oops: strange id" if $len < 4; $i = $len - 1; return -1 if !defined($id0->[$i]); return 1 if !defined($id1->[$i]); return $id0->[$i] <=> $id1->[$i]; } die "internal oops: can't compare that!"; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # sub cmp_skey { my ($skey0, $skey1, $len0, $len1, $len, $i, $k); ($skey0, $skey1) = @_; return 0 if !defined($skey0) && !defined($skey1); return -1 if !defined($skey0); return 1 if !defined($skey1); $len0 = @$skey0; $len1 = @$skey1; $len = $len0 < $len1 ? $len0 : $len1; # $dump->dumpValue( $skey0 ); # $dump->dumpValue( $skey1 ); for($i = 0; $i < $len; $i++) { next unless defined($skey0->[$i]) || defined($skey1->[$i]); # note: this looks reversed, but is intentional! return 1 if !defined($skey0->[$i]); return -1 if !defined($skey1->[$i]); $k = cmp_id $skey0->[$i], $skey1->[$i]; return $k if $k; } return $len0 <=> $len1; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # 0: equal # +-1: differing keys # +-2: differing values # sub cmp_item { my ($item0, $item1, $len0, $len1, $len, $i, $k); ($item0, $item1) = @_; $cmp_item_cnt++; return 0 if !defined($item0) && !defined($item1); return -1 if !defined($item0); return 1 if !defined($item1); $len0 = @{$item0->[1]}; $len1 = @{$item1->[1]}; $len = $len0 < $len1 ? $len0 : $len1; # $dump->dumpValue( $item0 ); for($i = 0; $i < $len; $i++) { return -1 if !defined($item0->[1][$i]); return 1 if !defined($item1->[1][$i]); $k = cmp_skey $item0->[1][$i], $item1->[1][$i]; # print " skey: $k\n"; return $k if $k; } $k = $len0 <=> $len1; return $k if $k; return 0 if !defined($item0->[2]) && !defined($item1->[2]); return -2 if !defined($item0->[2]); return 2 if !defined($item1->[2]); $k = cmp_skey $item0->[2], $item1->[2]; # print " val: $k\n"; return 2 * $k; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # check if id1 is part of id0 # # return: # 1: yes # 0: no # undef: don't know # # hd: [ "source", [ skey, skey, ... ], [ val ] ] # skey/val: [ ... , id, ..., id, ... ] # id: [ $flag_id, $tag, $value, $range, $mask ] # id: [ $flag_string, "str", "str", ... ] sub match_id { my ($id0, $id1, $len0, $len1, $len, $i, $k); ($id0, $id1) = @_; return 0 if !defined($id0) || !defined($id1); return 0 if $id0->[0] != $id1->[0]; $len0 = @$id0; $len1 = @$id1; $len = $len0 < $len1 ? $len0 : $len1; if($id0->[0] == $flag_string) { for($i = 1; $i < $len; $i++) { return 0 if $id0->[$i] cmp $id1->[$i]; } return $len0 != $len1 ? 0 : 1; } if($id0->[0] == $flag_id) { return 0 if $id0->[1] != $id1->[1]; if($len1 == 3) { if($len0 == 3) { return $id0->[2] != $id1->[2] ? 0 : 1; } elsif($len0 == 4) { return $id1->[2] >= $id0->[2] && $id1->[2] < $id0->[2] + $id0->[3] ? 1 : 0; } elsif($len0 == 5) { return ($id1->[2] & ~$id0->[4]) == $id0->[2] ? 1 : 0; } else { die "invalid id"; } } elsif($len1 == 4) { return undef; } elsif($len1 == 5) { return undef; } else { die "invalid id"; } } die "internal oops: can't match that!"; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # skey1 part of skey0? # sub match_skey { my ($skey0, $skey1, $len0, $len1, $len, $i, $k); ($skey0, $skey1) = @_; return 0 if !defined($skey0) || !defined($skey1); $len0 = @$skey0; $len1 = @$skey1; $len = $len0 > $len1 ? $len0 : $len1; # $dump->dumpValue( $skey0 ); # $dump->dumpValue( $skey1 ); for($i = 0; $i < $len; $i++) { next unless defined($skey1->[$i]); return 0 if !defined($skey0->[$i]) && defined($skey1->[$i]); $k = match_id $skey0->[$i], $skey1->[$i]; return $k if !$k; } return 1; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # item1 part of item0? # sub match_item { my ($item0, $item1, $len0, $len1, $i, $j, $k, $m); ($item0, $item1) = @_; $match_item_cnt++; return 0 if !defined($item0) || !defined($item1); $len0 = @{$item0->[1]}; $len1 = @{$item1->[1]}; for($j = 0; $j < $len1; $j++) { for($i = 0; $i < $len0; $i++) { $k = match_skey $item0->[1][$i], $item1->[1][$j]; $m = $k if defined $k; return $k if $k; } } return $m } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # add skey1 to skey0 # sub join_skey { my ($skey0, $skey1, $len, $i, $k, $n, $buf, $err); ($skey0, $skey1, $buf, $errors) = @_; $$errors = 0; return undef if !defined($skey0) && !defined($skey1); return [ @$skey0 ] if !defined($skey1); return [ @$skey1 ] if !defined($skey0); $n = [ @$skey0 ]; $len = @$skey1; for($i = 0; $i < $len; $i++) { next unless defined $skey1->[$i]; $n->[$i] = $skey1->[$i]; next unless defined $skey0->[$i]; $k = cmp_id $skey0->[$i], $skey1->[$i]; if($k) { if(defined $buf) { if($i != $he_driver) { $$buf .= ent_name_pr(" 0:", $ent_names[$i]); $$buf .= id_dump($i, $skey0->[$i]) . "\n"; $$buf .= ent_name_pr(" 1:", $ent_names[$i]); $$buf .= id_dump($i, $skey1->[$i]) . "\n"; } else { $$buf .= drv_dump(" 0:", $skey0->[$i]); $$buf =~ s/\n&/\n 0:/; $$buf .= drv_dump(" 1:", $skey1->[$i]); $$buf =~ s/\n&/\n 1:/; } } $$errors++ if defined $errors; } } return $n; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # split key fields # sub split_item { my ($item, @items, $tmp); local $_; $item = shift; return $item if !defined($item) || !defined($item->[1]); for (@{$item->[1]}) { $tmp = [ @$item ]; $tmp->[1] = [ $_ ]; push @items, $tmp; } return @items; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub get_xml_data { my ($xml, $i, $j); $xml = shift; if($xml->[0] ne 'hwdata') { die "invalid XML root element (expected 'hwdata')\n" } for($i = 1; $i < @{$xml->[1]}; $i += 2) { if($xml->[1][$i] eq 'item') { push @hd, parse_xml_item($xml->[1][$i + 1]); } } } sub parse_xml_item { my (@xml, %attr, $i, $item); @xml = @{$_[0]}; %attr = %{shift @xml}; for($i = 0; $i < @xml; $i += 2) { if($xml[$i] eq 'key') { push @{$item->[1]}, parse_xml_key($xml[$i + 1]); } else { $item->[2] = parse_xml_key($_[0]); } } return $item; } sub parse_xml_key { my (@xml, %attr, $i, @key, $val, $id, $is_id, $keyid, $keyid2, $tmp); @xml = @{$_[0]}; %attr = %{shift @xml}; for($i = 0; $i < @xml; $i += 2) { next if $xml[$i] eq '0' || $xml[$i] eq 'key'; $keyid = $xml_values{$xml[$i]}; $is_id = $keyid < $he_nomask && $ent_names[$keyid] =~ /\.(id|name)$/ ? 1 : 0; if(!defined($keyid)) { die "invalid key element \"$xml[$i]\"\n"; } if($keyid == $he_driver) { $id = parse_xml_driver($xml[$i + 1]); if(!defined($key[$keyid])) { $key[$keyid] = $id; } else { push @{$key[$keyid]}, $id->[1]; } } elsif($is_id) { $id = parse_xml_id($xml[$i + 1]); if($id->[0] == $flag_id) { $tmp = $ent_names[$keyid]; $tmp =~ s/\.name$/.id/; $keyid2 = $ent_values{$tmp}; if(!defined($keyid2)) { die "oops, no .id for $xml[$i]?"; } } else { $tmp = $ent_names[$keyid]; $tmp =~ s/\.id$/.name/; $keyid2 = $ent_values{$tmp}; if(!defined($keyid2)) { die "oops, no .name for $xml[$i]?"; } } $key[$keyid2] = $id; } else { $val = parse_xml_cdata($xml[$i + 1]); if(defined($key[$keyid]) && $keyid == $he_requires) { $key[$keyid][1] .= "|$val"; } else { $key[$keyid] = [ $flag_string, $val ]; } } } return [ @key ]; } sub parse_xml_id { my (@xml, %attr, $i, $id, $val); @xml = @{$_[0]}; %attr = %{shift @xml}; for($i = 0; $i < @xml; $i += 2) { next if $xml[$i] eq '0'; if($xml[$i] eq 'id') { $id = parse_xml_id_id($xml[$i + 1]); } elsif($xml[$i] eq 'idrange') { $id = parse_xml_id_range($xml[$i + 1]); } elsif($xml[$i] eq 'idmask') { $id = parse_xml_id_mask($xml[$i + 1]); } elsif($xml[$i] eq 'name') { $val = parse_xml_cdata($xml[$i + 1]); $id = [ $flag_string, $val ]; } else { die "invalid id element \"$xml[$i]\"\n"; } } return $id; } sub parse_xml_id_id { my (@xml, %attr, $i, $tag, $value); @xml = @{$_[0]}; %attr = %{shift @xml}; $tag = $tag_values{$attr{type}}; if(!defined($tag)) { die "missing/unsupported id attribute \"$attr{type}\"\n"; } for($i = 0; $i < @xml; $i += 2) { if($xml[$i] eq '0') { $value = idstr2value $tag, $xml[$i + 1]; } else { die "cdata expected, got \"$xml[$i]\"\n"; } } return [ $flag_id, $tag, $value ]; } sub parse_xml_id_range { my (@xml, %attr, $i, $tag, $value, $range); @xml = @{$_[0]}; %attr = %{shift @xml}; $tag = $tag_values{$attr{type}}; if(!defined($tag)) { die "missing/unsupported id attribute \"$attr{type}\"\n"; } for($i = 0; $i < @xml; $i += 2) { next if $xml[$i] eq '0'; if($xml[$i] eq 'first') { $value = idstr2value $tag, parse_xml_cdata($xml[$i + 1]); } elsif($xml[$i] eq 'last') { $range = idstr2value $tag, parse_xml_cdata($xml[$i + 1]); } else { die "invalid idrange element \"$xml[$i]\"\n"; } } if(!defined($value) || !defined($range)) { die "invalid idrange\n"; } return [ $flag_id, $tag, $value, $range - $value + 1 ]; } sub parse_xml_id_mask { my (@xml, %attr, $i, $tag, $value, $mask); @xml = @{$_[0]}; %attr = %{shift @xml}; $tag = $tag_values{$attr{type}}; if(!defined($tag)) { die "missing/unsupported id attribute \"$attr{type}\"\n"; } for($i = 0; $i < @xml; $i += 2) { next if $xml[$i] eq '0'; if($xml[$i] eq 'value') { $value = idstr2value $tag, parse_xml_cdata($xml[$i + 1]); } elsif($xml[$i] eq 'mask') { $mask = idstr2value $tag, parse_xml_cdata($xml[$i + 1]); } else { die "invalid idmask element \"$xml[$i]\"\n"; } } if(!defined($value) || !defined($mask)) { die "invalid idmask\n"; } return [ $flag_id, $tag, $value, undef, $mask ]; } sub parse_xml_driver { my (@xml, %attr, $i, $val); @xml = @{$_[0]}; %attr = %{shift @xml}; for($i = 0; $i < @xml; $i += 2) { next if $xml[$i] eq '0'; if($xml[$i] eq 'any') { $val = "a\t" . parse_xml_cdata($xml[$i + 1]); } elsif($xml[$i] eq 'display') { $val = parse_xml_driver_display($xml[$i + 1]); } elsif($xml[$i] eq 'module') { $val = parse_xml_driver_module($xml[$i + 1]); } elsif($xml[$i] eq 'mouse') { $val = parse_xml_driver_mouse($xml[$i + 1]); } elsif($xml[$i] eq 'xfree') { $val = parse_xml_driver_xfree($xml[$i + 1]); } else { die "invalid driver element \"$xml[$i]\"\n"; } } return [ $flag_string, $val ]; } sub parse_xml_driver_display { my (@xml, %attr, $i, @val); @xml = @{$_[0]}; %attr = %{shift @xml}; for($i = 0; $i < @xml; $i += 2) { next if $xml[$i] eq '0'; if($xml[$i] eq 'resolution') { $val[0] = join('x', parse_xml_pair($xml[$i + 1], 'width', 'height')); } elsif($xml[$i] eq 'vsync') { $val[1] = join('-', parse_xml_pair($xml[$i + 1], 'min', 'max')); } elsif($xml[$i] eq 'hsync') { $val[2] = join('-', parse_xml_pair($xml[$i + 1], 'min', 'max')); } elsif($xml[$i] eq 'bandwidth') { $val[3] = parse_xml_cdata($xml[$i + 1]); } else { die "invalid display element \"$xml[$i]\"\n"; } } if(!@val) { die "invalid display info\n"; } return "d\t" . join('|', @val); } sub parse_xml_driver_module { my (@xml, %attr, $i, $val, $type, @conf, @mods); @xml = @{$_[0]}; %attr = %{shift @xml}; for($i = 0; $i < @xml; $i += 2) { next if $xml[$i] eq '0'; $val = parse_xml_cdata($xml[$i + 1]); if($xml[$i] eq 'modprobe') { if($type && $type ne 'm') { die "invalid module info: \"$xml[$i]\"\n"; } $type = 'm'; push @mods, $val; } elsif($xml[$i] eq 'insmod') { if($type && $type ne 'i') { die "invalid module info: \"$xml[$i]\"\n"; } $type = 'i'; push @mods, $val; } elsif($xml[$i] eq 'modconf') { if($type && $type ne 'm') { die "invalid module info: \"$xml[$i]\"\n"; } push @conf, "\x00M\t$val"; } else { die "invalid module element \"$xml[$i]\"\n"; } } if(!$type && !@mods) { die "invalid module info\n"; } $val = "$type\t" . join('|', @mods); if(@conf) { $val .= join('', @conf); } return $val; } sub parse_xml_driver_mouse { my (@xml, %attr, $i, $val, @val); @xml = @{$_[0]}; %attr = %{shift @xml}; for($i = 0; $i < @xml; $i += 2) { next if $xml[$i] eq '0'; $val = parse_xml_cdata($xml[$i + 1]); if($xml[$i] eq 'xf86') { $val[0] = $val; } elsif($xml[$i] eq 'gpm') { $val[1] = $val; } elsif($xml[$i] eq 'buttons') { $val[2] = $val; } elsif($xml[$i] eq 'wheels') { $val[3] = $val; } else { die "invalid mouse element \"$xml[$i]\"\n"; } } if(!@val) { die "invalid mouse info\n"; } return "p\t" . join('|', @val); } sub parse_xml_driver_xfree { my (@xml, %attr, $i, $val, @val, @conf); @xml = @{$_[0]}; %attr = %{shift @xml}; for($i = 0; $i < @xml; $i += 2) { next if $xml[$i] eq '0'; if($xml[$i] eq 'has3d') { $val[2] = '3d'; } else { $val = parse_xml_cdata($xml[$i + 1]); if($xml[$i] eq 'version') { $val[0] = $val; } elsif($xml[$i] eq 'server') { $val[1] = $val; } elsif($xml[$i] eq 'extension') { $val[4] .= "," if defined $val[4]; $val[4] .= $val; } elsif($xml[$i] eq 'option') { $val[5] .= "," if defined $val[5]; $val[5] .= $val; } elsif($xml[$i] eq 'bpp') { $val[6] .= "," if defined $val[6]; $val[6] .= $val; } elsif($xml[$i] eq 'dacspeed') { $val[7] = $val; } elsif($xml[$i] eq 'script') { $val[8] = $val; } elsif($xml[$i] eq 'xf86conf') { push @conf, "\x00X\t$val"; } else { die "invalid xfree element \"$xml[$i]\"\n"; } } } if(!@val) { die "invalid xfree info\n"; } $val = "x\t" . join('|', @val); if(@conf) { $val .= join('', @conf); } return $val; } sub parse_xml_pair { my (@xml, %attr, $i, $val0, $val1, $elem0, $elem1); $elem0 = $_[1]; $elem1 = $_[2]; @xml = @{$_[0]}; %attr = %{shift @xml}; for($i = 0; $i < @xml; $i += 2) { next if $xml[$i] eq '0'; if($xml[$i] eq $elem0) { $val0 = parse_xml_cdata($xml[$i + 1]); } elsif($xml[$i] eq $elem1) { $val1 = parse_xml_cdata($xml[$i + 1]); } else { die "invalid element \"$xml[$i]\"\n"; } } if(!defined($val0) || !defined($val1)) { die "invalid element\n"; } return ($val0, $val1); } sub parse_xml_cdata { my (@xml, %attr, $i); @xml = @{$_[0]}; %attr = %{shift @xml}; for($i = 0; $i < @xml; $i += 2) { if($xml[$i] eq '0') { return $xml[$i + 1] } } } sub idstr2value { my ($tag, $value); ($tag, $value) = @_; if($tag == $tag_eisa && length($value) == 3 && $value !~ /^[0-9]/) { $value = eisa_id $value; } else { $value = num $value; } return $value; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub ent_name_pr { my ($str, $len); $str = $_[0] . $_[1]; $len = length $str; $str .= "\t"; $len = ($len & ~7) + 8; $str .= "\t" x ((24 - $len)/8) if $len < 24; return $str; } sub id_dump { my ($id, $ent, $str, $tag, $format); ($ent, $id) = @_; if($id->[0] == $flag_id) { $tag = $id->[1]; if($tag == $tag_eisa && ($ent == $he_vendor_id || $ent == $he_subvendor_id)) { $str = eisa_str $id->[2]; } else { $str .= $tag_name[$tag]; $str .= " " if $tag; $format = "0x%04x"; $format = "0x%02x" if $ent == $he_bus_id || $ent == $he_subclass_id || $ent == $he_progif_id; $format = "0x%03x" if $ent == $he_baseclass_id; $str .= sprintf $format, $id->[2]; } if(defined $id->[3]) { $str .= sprintf "+0x%04x", $id->[3]; } elsif(defined $id->[4]) { $str .= sprintf "&0x%04x", $id->[4]; } } elsif($id->[0] == $flag_string) { if(defined($id->[2])) { die "oops: strage string data\n"; } $str = $id->[1]; } else { die "oops: unknown id flag\n" } return $str; } sub drv_dump { my ($id, $str, $i, $pre, $type, $drv, $buf); ($pre, $id) = @_; die "oops: invalid driver data\n" if $id->[0] != $flag_string; for($i = 1; $i < @{$id}; $i++) { for $drv (split /\x00/, $id->[$i]) { $type = substr $drv, 0, 2; if($type eq "x\t") { $buf .= ent_name_pr($pre, $ent_names[$he_driver_xfree]); $buf .= substr($drv, 2) . "\n"; } elsif($type eq "X\t") { $buf .= ent_name_pr($pre, $ent_names[$he_driver_xfree_config]); $buf .= substr($drv, 2) . "\n"; } elsif($type eq "i\t") { $buf .= ent_name_pr($pre, $ent_names[$he_driver_module_insmod]); $buf .= substr($drv, 2) . "\n"; } elsif($type eq "m\t") { $buf .= ent_name_pr($pre, $ent_names[$he_driver_module_modprobe]); $buf .= substr($drv, 2) . "\n"; } elsif($type eq "M\t") { $buf .= ent_name_pr($pre, $ent_names[$he_driver_module_config]); $buf .= substr($drv, 2) . "\n"; } elsif($type eq "p\t") { $buf .= ent_name_pr($pre, $ent_names[$he_driver_mouse]); $buf .= substr($drv, 2) . "\n"; } elsif($type eq "d\t") { $buf .= ent_name_pr($pre, $ent_names[$he_driver_display]); $buf .= substr($drv, 2) . "\n"; } elsif($type eq "a\t") { $buf .= ent_name_pr($pre, $ent_names[$he_driver_any]); $buf .= substr($drv, 2) . "\n"; } else { die "oops: unhandled driver info type: $drv\n"; } $pre = "&" if $pre ne "+"; } } return $buf; } sub ent_dump { my ($pre, $id, $ent, $buf); ($buf, $pre, $id) = @_; $pre = defined($pre) ? "|" : " " if $pre ne "+"; for($ent = 0; $ent < @{$id}; $ent++) { if(defined $id->[$ent]) { if($ent != $he_driver) { $$buf .= ent_name_pr($pre, $ent_names[$ent]); $$buf .= id_dump($ent, $id->[$ent]); $$buf .= "\n"; } else { $$buf .= drv_dump($pre, $id->[$ent]); } $pre = "&" if $pre ne "+"; } } return $pre; } sub dump2ids { my ($item, $id, $ent, $pre, $buf); # $dump->dumpValue( \@hd ); open F, ">hd.ids"; for $item (@hd) { undef $buf; undef $pre; print F "# $item->[0]\n" if $opt_with_source; for $id (@{$item->[1]}) { $pre = ent_dump \$buf, $pre, $id; } $pre = "+"; ent_dump \$buf, $pre, $item->[2]; $buf .= "\n"; print F $buf; } close F; } # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sub dump2xml { my ($item, $dtd); if($opt_internal_dtd) { $dtd = hd_dtd_internal; } else { $dtd = "\n"; } $xml_file = new IO::File(">hd.xml"); $xml = new XML::Writer(OUTPUT => $xml_file, DATA_MODE => 1, DATA_INDENT => 2); $xml->xmlDecl("utf-8"); print $xml_file "\n$dtd"; $xml->startTag("hwdata"); print $xml_file "\n"; for $item (@hd) { dump_xml_item $item; } $xml->endTag("hwdata"); $xml->end(); if(!$opt_internal_dtd) { print STDERR "writing \"hd.dtd\"\n"; open DTD, ">hd.dtd"; print DTD hd_dtd; close DTD; } } sub dump_xml_id { my ($ent, $id, $i, $tag, $str, $format, $range, $mask); ($ent, $id) = @_; $i = $xml_names[$ent]; die "oops: entry $ent not allowed here\n" unless $i; if($ent == $he_requires) { if($id->[0] == $flag_string) { die "oops: strange string data\n" if defined $id->[2]; for $str (split /\|/, $id->[1]) { $xml->dataElement("requires", $str); } } else { die "oops: requires _id_???\n" } } else { $xml->startTag($i); if($ent == $he_serial) { if($id->[0] == $flag_string) { die "oops: strange string data\n" if defined $id->[2]; $xml->characters($id->[1]); } else { die "oops: serial _id_???\n" } } else { if($id->[0] == $flag_id) { $tag = $id->[1]; if($tag == $tag_eisa && ($ent == $he_vendor_id || $ent == $he_subvendor_id)) { $str = eisa_str $id->[2]; } else { $format = "0x%04x"; $format = "0x%02x" if $ent == $he_bus_id || $ent == $he_subclass_id || $ent == $he_progif_id; $format = "0x%03x" if $ent == $he_baseclass_id; $str = sprintf $format, $id->[2]; } if(defined $id->[3]) { if($tag == $tag_eisa && ($ent == $he_vendor_id || $ent == $he_subvendor_id)) { $range = eisa_str $id->[2] + $id->[3] - 1; } else { $range = sprintf "0x%04x", $id->[2] + $id->[3] - 1; } } elsif(defined $id->[4]) { $mask = sprintf "0x%04x", $id->[4]; } $tag = $tag_name[$tag]; if(defined $range) { if($tag) { $xml->startTag("idrange", "type" => $tag); } else { $xml->startTag("idrange"); } $xml->dataElement("first", $str); $xml->dataElement("last", $range); $xml->endTag(); } elsif(defined $mask) { if($tag) { $xml->startTag("idmask", "type" => $tag); } else { $xml->startTag("idmask"); } $xml->dataElement("value", $str); $xml->dataElement("mask", $mask); $xml->endTag(); } else { if($tag) { $xml->dataElement("id", $str, "type" => $tag); } else { $xml->dataElement("id", $str); } } } elsif($id->[0] == $flag_string) { die "oops: strage string data\n" if defined $id->[2]; $xml->dataElement("name", $id->[1]); } else { die "oops: unknown id flag\n" } } $xml->endTag(); } } sub dump_xml_drv { my ($id, $str, $i, $j, $k, $type, $drv, $info, @info, $current); $id = shift; die "oops: invalid driver data\n" if $id->[0] != $flag_string; for($i = 1; $i < @{$id}; $i++) { $xml->startTag('driver'); undef $current; for $drv (split /\x00/, $id->[$i]) { $type = substr $drv, 0, 2; $info = substr $drv, 2; @info = split /\|/, $info; if($type eq "i\t") { $xml->endTag() if $current; $current = $type; $xml->startTag('module'); for $j (@info) { $xml->dataElement('insmod', $j); } } elsif($type eq "m\t") { $xml->endTag() if $current; $current = $type; $xml->startTag('module'); for $j (@info) { $xml->dataElement('modprobe', $j); } } elsif($type eq "M\t") { die "oops: incorrect driver info: $drv\n" unless $current eq "m\t"; $xml->dataElement('modconf', $info); } elsif($type eq "a\t") { $xml->endTag() if $current; $current = undef;; $xml->dataElement('any', $info); } elsif($type eq "d\t") { $xml->endTag() if $current; $current = undef; $xml->startTag('display'); if($info[0] =~ /^(\d+)x(\d+)$/) { ($j, $k) = ($1, $2); $xml->startTag('resolution'); $xml->dataElement('width', $j); $xml->dataElement('height', $k); $xml->endTag('resolution'); } if($info[1] =~ /^(\d+)-(\d+)$/) { ($j, $k) = ($1, $2); $xml->startTag('vsync'); $xml->dataElement('min', $j); $xml->dataElement('max', $k); $xml->endTag('vsync'); } if($info[2] =~ /^(\d+)-(\d+)$/) { ($j, $k) = ($1, $2); $xml->startTag('hsync'); $xml->dataElement('min', $j); $xml->dataElement('max', $k); $xml->endTag('hsync'); } if($info[3] =~ /^\d+$/) { $xml->dataElement('bandwidth', $info[3]); } $xml->endTag('display'); } elsif($type eq "x\t") { $xml->endTag() if $current; $current = $type; $xml->startTag('xfree'); if(defined $info[0]) { $xml->dataElement('version', $info[0]); } if($info[1]) { $xml->dataElement('server', $info[1]); } if($info[2]) { $xml->emptyTag('has3d'); } # if($info[3]) { # for $j (split /,/, $info[3]) { # $xml->dataElement('package', $j); # } # } if($info[4]) { for $j (split /,/, $info[4]) { $xml->dataElement('extension', $j); } } if($info[5]) { for $j (split /,/, $info[5]) { $xml->dataElement('option', $j); } } if($info[6]) { for $j (split /,/, $info[6]) { $xml->dataElement('bpp', $j); } } if($info[7] =~ /^\d+$/) { $xml->dataElement('dacspeed', $info[7]); } if($info[8]) { $xml->dataElement('script', $info[8]); } } elsif($type eq "X\t") { die "oops: incorrect driver info: $drv\n" unless $current eq "x\t"; $xml->dataElement('xf86conf', $info); } elsif($type eq "p\t") { $xml->endTag() if $current; $current = undef; $xml->startTag('mouse'); if($info[0]) { $xml->dataElement('xf86', $info[0]); } if($info[1]) { $xml->dataElement('gpm', $info[1]); } if($info[2] ne "") { $xml->dataElement('buttons', $info[2]); } if($info[3] ne "") { $xml->dataElement('wheels', $info[3]); } $xml->endTag('mouse'); } else { $xml->endTag() if $current; $current = undef; # die "oops: unhandled driver info type: $drv\n"; } } $xml->endTag() if $current; $xml->endTag('driver'); } } sub dump_xml_ent { my ($id, $ent); $id = shift; for($ent = 0; $ent < @{$id}; $ent++) { if(defined $id->[$ent]) { if($ent != $he_driver) { dump_xml_id $ent, $id->[$ent]; } else { dump_xml_drv $id->[$ent]; } } } } sub dump_xml_item { my ($item, $id); $item = shift; $xml->startTag('item'); for $id (@{$item->[1]}) { $xml->startTag('key'); dump_xml_ent $id; $xml->endTag('key'); } dump_xml_ent $item->[2]; $xml->endTag('item'); print $xml_file "\n"; } sub hd_dtd { my $dtd = <<'EOF' EOF ; return $dtd; } sub hd_dtd_internal { my $dtd = <<'EOF' ]> EOF ; return $dtd; }