3
# Parse PCI_ROM and ISA_ROM entries from source file(s) specified as
4
# arguments and output the relevant Makefile rules to STDOUT.
6
# Originally based on portions of Ken Yap's genrules.pl. Completely
7
# rewritten by Robin Smidsrød to be more maintainable.
13
# Parse command-line options
14
my @exclude_driver_classes = ();
15
my @exclude_drivers = ();
19
"exclude-driver-class=s" => \@exclude_driver_classes,
20
"exclude-driver=s" => \@exclude_drivers,
25
# Convert exclution arrays to lookup tables
26
my $exclude_driver_class_map = { map { $_ => 1 } @exclude_driver_classes };
27
my $exclude_driver_map = { map { $_ => 1 } @exclude_drivers };
29
# Ensure STDOUT and STDERR are synchronized if debugging
35
# Compile regular expressions here for slight performance boost
37
'parse_driver_class' => qr{ drivers/ (\w+?) / }x,
38
'parse_family' => qr{^ (?:\./)? (.*) \..+? $}x,
39
'find_rom_line' => qr/^ \s* ( (PCI|ISA)_ROM \s* \( \s* (.*?) ) $/x,
40
'extract_pci_id' => qr/^ \s* 0x([0-9A-Fa-f]{4}) \s* ,? \s* (.*) $/x,
41
'extract_quoted_string' => qr/^ \s* \" ([^\"]*?) \" \s* ,? \s* (.*) $/x,
44
# Show help if required arguments are missing or help was requested
45
show_usage_and_exit() if $help or @ARGV < 1;
47
# Process each source file specified
48
process_source_file($_) for @ARGV;
52
sub show_usage_and_exit {
54
Syntax: $0 [<options>] <source-file> [<source-file>]
56
--exclude-driver-class Exclude specified driver classes
57
--exclude-driver Exclude specified drivers
58
--debug Output debug information on STDERR
59
--help This help information
64
# Figure out if source file is a driver and look for ROM declarations
65
sub process_source_file {
66
my ($source_file) = @_;
67
return unless defined $source_file;
68
return unless length $source_file;
69
my $state = { 'source_file' => $source_file };
70
log_debug("SOURCE_FILE", $state->{source_file});
71
# Skip source files that aren't drivers
72
parse_driver_class( $state );
73
unless ( $state->{'driver_class'} ) {
74
log_debug("SKIP_NOT_DRIVER", $state->{source_file} );
77
# Skip source files with driver classes that are explicitly excluded
78
if ( $exclude_driver_class_map->{ $state->{'driver_class'} } ) {
79
log_debug("SKIP_EXCL_CLASS", $state->{'driver_class'} );
82
# Skip source files without driver information
83
parse_family( $state );
84
parse_driver_name( $state );
85
unless ( $state->{'family'} and $state->{'driver_name'} ) {
86
log_debug("SKIP_NO_DRV_INFO", $state->{source_file} );
89
# Skip source files with drivers that are explicitly excluded
90
if ( $exclude_driver_map->{ $state->{'driver_name'} } ) {
91
log_debug("SKIP_EXCL_DRV", $state->{'driver_name'} );
94
# Iterate through lines in source files looking for ROM declarations
95
# and # output Makefile rules
96
open( my $fh, "<", $state->{'source_file'} )
97
or die "Couldn't open $state->{source_file}: $!\n";
99
process_rom_decl($state, $1, $2, $3) if m/$RE{find_rom_line}/;
101
close($fh) or die "Couldn't close $source_file: $!\n";
105
# Verify that the found ROM declaration is sane and dispatch to the right
106
# handler depending on type
107
sub process_rom_decl {
108
my ($state, $rom_line, $rom_type, $rom_decl) = @_;
109
return unless defined $rom_line;
110
return unless length $rom_line;
111
log_debug("ROM_LINE", $rom_line);
112
return unless defined $rom_type;
113
return unless length $rom_type;
114
log_debug("ROM_TYPE", $rom_type);
115
$state->{'type'} = lc $rom_type;
116
return process_pci_rom($state, $rom_decl) if $rom_type eq "PCI";
117
return process_isa_rom($state, $rom_decl) if $rom_type eq "ISA";
121
# Extract values from PCI_ROM declaration lines and dispatch to
122
# Makefile rule generator
123
sub process_pci_rom {
124
my ($state, $decl) = @_;
125
return unless defined $decl;
126
return unless length $decl;
127
(my $vendor, $decl) = extract_pci_id($decl, 'PCI_VENDOR');
128
(my $device, $decl) = extract_pci_id($decl, 'PCI_DEVICE');
129
(my $image, $decl) = extract_quoted_string($decl, 'IMAGE');
130
(my $desc, $decl) = extract_quoted_string($decl, 'DESCRIPTION');
131
if ( $vendor and $device and $image and $desc ) {
132
print_make_rules( $state, "${vendor}${device}", $desc, $vendor, $device );
133
print_make_rules( $state, $image, $desc, $vendor, $device, 1 );
136
log_debug("WARNING", "Malformed PCI_ROM macro on line $. of $state->{source_file}");
141
# Extract values from ISA_ROM declaration lines and dispatch to
142
# Makefile rule generator
143
sub process_isa_rom {
144
my ($state, $decl) = @_;
145
return unless defined $decl;
146
return unless length $decl;
147
(my $image, $decl) = extract_quoted_string($decl, 'IMAGE');
148
(my $desc, $decl) = extract_quoted_string($decl, 'DESCRIPTION');
149
if ( $image and $desc ) {
150
print_make_rules( $state, $image, $desc );
153
log_debug("WARNING", "Malformed ISA_ROM macro on line $. of $state->{source_file}");
158
# Output Makefile rules for the specified ROM declarations
159
sub print_make_rules {
160
my ( $state, $image, $desc, $vendor, $device, $dup ) = @_;
161
unless ( $state->{'is_header_printed'} ) {
163
print "# NIC\tfamily\t$state->{family}\n";
164
print "DRIVERS_$state->{driver_class} += $state->{driver_name}\n";
165
print "DRIVERS += $state->{driver_name}\n";
167
$state->{'is_header_printed'} = 1;
169
return if $vendor and ( $vendor eq "ffff" or $device eq "ffff" );
170
my $ids = $vendor ? "$vendor,$device" : "-";
171
print "# NIC\t$image\t$ids\t$desc\n";
172
print "DRIVER_$image = $state->{driver_name}\n";
173
print "ROM_TYPE_$image = $state->{type}\n";
174
print "ROM_DESCRIPTION_$image = \"$desc\"\n";
175
print "PCI_VENDOR_$image = 0x$vendor\n" if $vendor;
176
print "PCI_DEVICE_$image = 0x$device\n" if $device;
177
print "ROMS += $image\n" unless $dup;
178
print "ROMS_$state->{driver_name} += $image\n" unless $dup;
183
# Driver class is whatever comes after the "drivers" part of the filename (relative path)
184
sub parse_driver_class {
186
my $filename = $state->{'source_file'};
187
return unless defined $filename;
188
return unless length $filename;
189
if ( $filename =~ m/$RE{parse_driver_class}/ ) {
190
log_debug("DRIVER_CLASS", $1);
191
$state->{'driver_class'} = $1;
196
# Family name is filename (relative path) without extension
199
my $filename = $state->{'source_file'};
200
return unless defined $filename;
201
return unless length $filename;
202
if ( $filename =~ m/$RE{parse_family}/ ) {
203
log_debug("FAMILY", $1);
204
$state->{'family'} = $1;
209
# Driver name is last part of family name
210
sub parse_driver_name {
212
my $family = $state->{'family'};
213
return unless defined $family;
214
return unless length $family;
215
my @parts = split "/", $family;
216
$state->{'driver_name'} = $parts[-1];
217
log_debug("DRIVER", $state->{'driver_name'});
221
# Extract a PCI vendor/device ID e.g. 0x8086, possibly followed by a comma
222
# Should always be 4-digit lower-case hex number
224
my ($str, $label) = @_;
225
return "", $str unless defined $str;
226
return "", $str unless length $str;
227
if ( $str =~ m/$RE{extract_pci_id}/ ) {
229
log_debug($label, $id);
235
# Extract a double-quoted string, possibly followed by a comma
236
sub extract_quoted_string {
237
my ($str, $label) = @_;
238
return "", $str unless defined $str;
239
return "", $str unless length $str;
240
if ( $str =~ m/$RE{extract_quoted_string}/ ) {
241
log_debug($label, $1);
247
# Output debug info to STDERR (off by default)
249
my ($label, $str) = @_;
250
return unless $debug;
251
return unless defined $str;
252
print STDERR "\n" if $label eq 'SOURCE_FILE';
254
if ( defined $label ) {
255
my $pad_count = 16 - length $label;
256
print STDERR $label . ":" . ( " " x $pad_count );
258
print STDERR $str . "\n";