Home | History | Annotate | Download | only in util
      1 #! /usr/bin/perl -w
      2 
      3 # get-pci-ids: extract pci vendor/device ids from linux net drivers
      4 
      5 # Copyright (C) 2003 Georg Baum <gbaum (at] users.sf.net>
      6 
      7 # This program is free software; you can redistribute it and/or modify
      8 # it under the terms of the GNU General Public License as published by
      9 # the Free Software Foundation; either version 2 of the License, or
     10 # (at your option) any later version.
     11 
     12 # This program is distributed in the hope that it will be useful,
     13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 # GNU General Public License for more details.
     16 
     17 # You should have received a copy of the GNU General Public License
     18 # along with this program; if not, write to the Free Software
     19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     20 
     21 
     22 # Known bugs/limitations:
     23 # - Does not recognize all drivers because some require special cflags.
     24 #   Fails also on some drivers that do belong to other architectures
     25 #   than the one of the machine this script is running on.
     26 #   This is currently not so important because all drivers that have an
     27 #   Etherboot counterpart are recognized.
     28 
     29 
     30 use strict;
     31 use File::Basename "dirname";
     32 use POSIX "uname";
     33 
     34 # Where to find the kernel sources
     35 my $kernel_src = "/usr/src/linux";
     36 
     37 if($#ARGV >= 0) {
     38 	$kernel_src = shift;
     39 }
     40 
     41 # Sanity checks
     42 if($#ARGV >= 0) {
     43 	print STDERR "Too many arguments.\n";
     44 	print STDERR "Usage: get-pci-ids [path to kernel sources]\n";
     45 	print STDERR "       /usr/src/linux is assumed if no path is given.\n";
     46 	exit 1;
     47 }
     48 
     49 unless(-f "$kernel_src/include/linux/version.h") {
     50 	print STDERR "Could not find $kernel_src/include/linux/version.h.\n";
     51 	print STDERR "$kernel_src is probably no Linux kernel source tree.\n";
     52 	exit 1;
     53 }
     54 
     55 # Flags that are needed to preprocess the drivers.
     56 # Some drivers need optimization
     57 my $cflags="-D__KERNEL__ -I$kernel_src/include -I$kernel_src/net/inet -O2";
     58 
     59 # The C preprocessor. It needs to spit out the preprocessed source on stdout.
     60 my $cpp="gcc -E";
     61 
     62 # List of drivers. We parse every .c file. It does not harm if it does not contain a driver.
     63 my @drivers = split /\s+/, `find $kernel_src/drivers/net -name '*.c' | sort`;
     64 
     65 # Kernel version
     66 my $version = `grep UTS_RELEASE $kernel_src/include/linux/version.h`;
     67 chomp $version;
     68 $version =~ s/\s*#define\s+UTS_RELEASE\s+"(\S+)".*$/$1/g;
     69 
     70 # Architecture
     71 my @uname = uname();
     72 
     73 
     74 # Print header
     75 print "# PCI vendor/device ids extracted from Linux $version on $uname[4] at " . gmtime() . "\n";
     76 
     77 my $driver;
     78 
     79 # Process the drivers
     80 foreach $driver (@drivers) {
     81 
     82 	# Preprocess to expand macros
     83 	my $command = "$cpp $cflags -I" . dirname($driver) . " $driver";
     84 	open  DRIVER, "$command |" or die "Could not execute\n\"$command\".\n";
     85 
     86 	# Extract the pci_device_id structure
     87 	my $found = 0;
     88 	my $line = "";
     89 	my @lines;
     90 	while(<DRIVER>) {
     91 		if(/^\s*static\s+struct\s+pci_device_id/) {
     92 			# This file contains a driver. Print the name.
     93 			$driver =~ s!$kernel_src/drivers/net/!!g;
     94 			print "\n$driver\n";
     95 			$found = 1;
     96 			next;
     97 		}
     98 		if($found == 1){
     99 			if(/\};/ or /{\s*0\s*,?\s*}/) {
    100 				# End of struct
    101 				$found = 0;
    102 			} else {
    103 				chomp;
    104 				if(/\}\s*,?\s*\n?$/) {
    105 					# This line contains a full entry or the last part of it.
    106 					$_ = $line . $_;
    107 					$line = "";
    108 					s/[,\{\};\(\)]//g;	# Strip punctuation
    109 					s/^\s+//g;		# Eat whitespace at beginning of line
    110 					tr[A-Z][a-z];		# Convert to lowercase
    111 					# Push the vendor and device id to @lines if this line is not empty.
    112 					# We ignore everything else that might be there
    113 					my ($vendor_id, $device_id, $remainder) = split /\W+/, $_, 3;
    114 					push @lines, "$vendor_id $device_id\n" if($vendor_id && $device_id);
    115 				} else {
    116 					# This line does contain a partial entry. Remember it.
    117 					$line .= "$_ ";
    118 				}
    119 			}
    120 		}
    121 	}
    122 	close DRIVER;		# No "or die", because $cpp fails on some files
    123 
    124 	# Now print out the sorted values
    125 	@lines = sort @lines;
    126 	my $lastline = "";
    127 	foreach(@lines) {
    128 		# Print each vendor/device id combination only once.
    129 		# Some drivers (e.g. e100) do contain subfamilies
    130 		print if($_ ne $lastline);
    131 		$lastline = $_;
    132 	}
    133 }
    134 
    135 
    136