freebsd-nq/sys/kern/makedevops.pl
Doug Rabson 8b2970bbe6 * Augment the interface language to allow arbitrary C code to be 'passed
through' to the C compiler.
* Allow the interface to specify a default implementation for methods.
* Allow 'static' methods which are not device specific.
* Add a simple scheme for probe routines to return a priority value. To
  make life simple, priority values are negative numbers (positive numbers
  are standard errno codes) with zero being the highest priority. The
  driver which returns the highest priority will be chosen for the device.
1999-05-10 17:06:14 +00:00

452 lines
14 KiB
Perl

#!/usr/bin/perl
#
# Copyright (c) 1992, 1993
# The Regents of the University of California. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
# must display the following acknowledgement:
# This product includes software developed by the University of
# California, Berkeley and its contributors.
# 4. Neither the name of the University nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# From @(#)vnode_if.sh 8.1 (Berkeley) 6/10/93
# From @(#)makedevops.sh 1.1 1998/06/14 13:53:12 dfr Exp $
# From @(#)makedevops.sh ?.? 1998/10/05
#
# $Id: makedevops.pl,v 1.6 1999/05/02 08:55:27 markm Exp $
#
# Script to produce device front-end sugar.
#
$debug = 0;
$cfile = 0; # by default do not produce any file type
$hfile = 0;
$keepcurrentdir = 1;
$line_width = 80;
# Process the command line
#
while ( $arg = shift @ARGV ) {
if ( $arg eq '-c' ) {
warn "Producing .c output files"
if $debug;
$cfile = 1;
} elsif ( $arg eq '-h' ) {
warn "Producing .h output files"
if $debug;
$hfile = 1;
} elsif ( $arg eq '-ch' || $arg eq '-hc' ) {
warn "Producing .c and .h output files"
if $debug;
$cfile = 1;
$hfile = 1;
} elsif ( $arg eq '-d' ) {
$debug = 1;
} elsif ( $arg eq '-p' ) {
warn "Will produce files in original not in current directory"
if $debug;
$keepcurrentdir = 0;
} elsif ( $arg eq '-l' ) {
if ( $line_width = shift @ARGV and $line_width > 0 ) {
warn "Line width set to $line_width"
if $debug;
} else {
die "Please specify a valid line width after -l";
}
} elsif ( $arg =~ m/\.m$/ ) {
warn "Filename: $arg"
if $debug;
push @filenames, $arg;
} else {
warn "$arg ignored"
if $debug;
}
}
# Validate the command line parameters
#
die "usage: $0 [-d] [-p] [-c|-h] srcfile
where -c produce only .c files
-h produce only .h files
-p use the path component in the source file for destination dir
-l set line width for output files [80]
-d switch on debugging
"
unless ($cfile or $hfile)
and $#filenames != -1;
# FIXME should be able to do this more easily
#
$tmpdir = $ENV{'TMPDIR'}; # environment variables
$tmpdir = $ENV{'TMP'}
if !$tmpdir;
$tmpdir = $ENV{'TEMP'}
if !$tmpdir;
$tmpdir = '/tmp' # look for a physical directory
if !$tmpdir and -d '/tmp';
$tmpdir = '/usr/tmp'
if !$tmpdir and -d '/usr/tmp';
$tmpdir = '/var/tmp'
if !$tmpdir and -d '/var/tmp';
$tmpdir = '.' # give up and use current dir
if !$tmpdir;
foreach $src ( @filenames ) {
# Names of the created files
$ctmpname = "$tmpdir/ctmp.$$";
$htmpname = "$tmpdir/htmp.$$";
($name, $path, $suffix) = &fileparse($src, '.m');
$path = '.'
if $keepcurrentdir;
$cfilename="$path/$name.c";
$hfilename="$path/$name.h";
warn "Processing from $src to $cfile / $hfile via $ctmp / $htmp"
if $debug;
die "Could not open $src, $!"
if !open SRC, "$src";
die "Could not open $ctmpname, $!"
if $cfile and !open CFILE, ">$ctmpname";
die "Could not open $htmpname, $!"
if $hfile and !open HFILE, ">$htmpname";
if ( $cfile ) {
# Produce the header of the C file
#
print CFILE "/*\n";
print CFILE " * This file is produced automatically.\n";
print CFILE " * Do not modify anything in here by hand.\n";
print CFILE " *\n";
print CFILE " * Created from\n";
print CFILE " * $src\n";
print CFILE " * with\n";
print CFILE " * $0\n";
print CFILE " */\n";
print CFILE "\n";
print CFILE "#include <sys/param.h>\n";
print CFILE "#include <sys/queue.h>\n";
print CFILE "#include <sys/sysctl.h>\n";
print CFILE "#include <sys/bus_private.h>\n";
}
if ( $hfile ) {
# Produce the header of the H file
#
print HFILE "/*\n";
print HFILE " * This file is produced automatically.\n";
print HFILE " * Do not modify anything in here by hand.\n";
print HFILE " *\n";
print HFILE " * Created from\n";
print HFILE " * $src\n";
print HFILE " * with\n";
print HFILE " * $0\n";
print HFILE " */\n";
print HFILE "\n";
}
%methods = (); # clear list of methods
$lineno = 0;
$error = 0; # to signal clean up and gerror setting
LINE: while ( $line = <SRC> ) {
$lineno++;
# take special notice of include directives.
#
if ( $line =~ m/^#\s*include\s+(["<])([^">]+)([">]).*/i ) {
warn "Included file: $1$2" . ($1 eq '<'? '>':'"')
if $debug;
print CFILE "#include $1$2" . ($1 eq '<'? '>':'"') . "\n"
if $cfile;
}
$line =~ s/#.*//; # remove comments
$line =~ s/^\s+//; # remove leading ...
$line =~ s/\s+$//; # remove trailing whitespace
if ( $line =~ m/^$/ ) { # skip empty lines
# nop
} elsif ( $line =~ m/^INTERFACE\s*([^\s;]*)(\s*;?)/i ) {
$intname = $1;
$semicolon = $2;
unless ( $intname =~ m/^[a-z_][a-z0-9_]*$/ ) {
warn $line
if $debug;
warn "$src:$lineno: Invalid interface name '$intname', use [a-z_][a-z0-9_]*";
$error = 1;
last LINE;
}
warn "$src:$lineno: semicolon missing at end of line, no problem"
if $semicolon !~ s/;$//;
warn "Interface $intname"
if $debug;
print HFILE '#ifndef _'.$intname."_if_h_\n"
if $hfile;
print HFILE '#define _'.$intname."_if_h_\n\n"
if $hfile;
print CFILE '#include "'.$intname.'_if.h"'."\n\n"
if $cfile;
} elsif ( $line =~ m/^CODE\s*{$/i ) {
$code = "";
$line = <SRC>;
while ( $line !~ m/^}/ ) {
$code .= $line;
$line = <SRC>;
$lineno++
}
if ( $cfile ) {
print CFILE $code;
}
} elsif ( $line =~ m/^(STATIC|)METHOD/i ) {
# Get the return type function name and delete that from
# the line. What is left is the possibly first function argument
# if it is on the same line.
#
# FIXME For compatibilities sake METHOD and METHODE is accepted.
#
if ( !$intname ) {
warn "$src:$lineno: No interface name defined";
$error = 1;
last LINE;
}
$line =~ s/^(STATIC|)METHODE?\s+([^{]+?)\s*{\s*//i;
$static = $1;
@ret = split m/\s+/, $2;
$name = pop @ret; # last element is name of method
$ret = join(" ", @ret); # return type
warn "Method: name=$name return type=$ret"
if $debug;
if ( !$name or !$ret ) {
warn $line
if $debug;
warn "$src:$lineno: Invalid method specification";
$error = 1;
last LINE;
}
unless ( $name =~ m/^[a-z_][a-z_0-9]*$/ ) {
warn $line
if $debug;
warn "$src:$lineno: Invalid method name '$name', use [a-z_][a-z0-9_]*";
$error = 1;
last LINE;
}
if ( defined($methods{$name}) ) {
warn "$src:$lineno: Duplicate method name";
$error = 1;
last LINE;
}
$methods{$name} = 'VIS';
while ( $line !~ m/}/ and $line .= <SRC> ) {
$lineno++
}
$default = "";
if ( $line !~ s/};?(.*)// ) { # remove first '}' and trailing garbage
# The '}' was not there (the rest is optional), so complain
warn "$src:$lineno: Premature end of file";
$error = 1;
last LINE;
}
$extra = $1;
if ( $extra =~ /\s*DEFAULT\s*([a-zA-Z_][a-zA-Z_0-9]*)\s*;/ ) {
$default = $1;
} else {
warn "$src:$lineno: Ignored '$1'" # warn about garbage at end of line
if $debug and $1;
}
# Create a list of variables without the types prepended
#
$line =~ s/^\s+//; # remove leading ...
$line =~ s/\s+$//; # ... and trailing whitespace
$line =~ s/\s+/ /; # remove double spaces
@arguments = split m/\s*;\s*/, $line;
@varnames = (); # list of varnames
foreach $argument (@arguments) {
next # skip argument if argument is empty
if !$argument;
@ar = split m/[*\s]+/, $argument;
if ( $#ar == 0 ) { # only 1 word in argument?
warn "$src:$lineno: no type for '$argument'";
$error = 1;
last LINE;
}
push @varnames, $ar[-1]; # last element is name of variable
};
warn 'Arguments: ' . join(', ', @arguments) . "\n"
. 'Varnames: ' . join(', ', @varnames)
if $debug;
$mname = $intname.'_'.$name; # method name
$umname = uc($mname); # uppercase method name
$arguments = join(", ", @arguments);
$varnames = join(", ", @varnames);
$default = "0" if $default eq "";
if ( $hfile ) {
# the method description
print HFILE "extern struct device_op_desc $mname\_desc;\n";
# the method typedef
print HFILE &format_line("typedef $ret $mname\_t($arguments);",
$line_width, ', ',
',',' ' x length("typedef $ret $mname\_t("))
. "\n";
# the method declaration
print HFILE "$mname\_t $umname;\n\n";
}
if ( $cfile ) {
# Print out the method desc
print CFILE "struct device_op_desc $mname\_desc = {\n";
print CFILE "\t0, 0, (devop_t) $default, \"$mname\"\n";
print CFILE "};\n\n";
# Print out the method itself
if ( 0 ) { # haven't chosen the format yet
print CFILE "$ret $umname($varnames)\n";
print CFILE "\t".join(";\n\t", @arguments).";\n";
} else {
print CFILE &format_line("$ret $umname($arguments)",
$line_width, ', ',
',', ' ' x length("$ret $umname(")) . "\n";
}
print CFILE "{\n";
if ($static) {
print CFILE &format_line("\t$mname\_t *m = ($mname\_t *) DRVOPMETH(driver, $mname);",
$line_width-8, ' = ', ' =', "\t\t")
. "\n";
} else {
print CFILE &format_line("\t$mname\_t *m = ($mname\_t *) DEVOPMETH(dev, $mname);",
$line_width-8, ' = ', ' =', "\t\t")
. "\n";
}
print CFILE "\t".($ret eq 'void'? '':'return ') . "m($varnames);\n";
print CFILE "}\n\n";
}
} else {
warn $line
if $debug;
warn "$src:$lineno: Invalid line encountered";
$error = 1;
last LINE;
}
} # end LINE
# print the final '#endif' in the header file
#
print HFILE "#endif /* _".$intname."_if_h_ */\n"
if $hfile;
close SRC;
close CFILE
if $cfile;
close HFILE
if $hfile;
if ( !$error ) {
if ( $cfile ) {
($rc = system("mv $ctmpname $cfilename"))
and warn "mv $ctmpname $cfilename failed, $rc";
}
if ( $hfile ) {
($rc = system("mv $htmpname $hfilename"))
and warn "mv $htmpname $hfilename failed, $rc";
}
} else {
warn 'File' . ($hfile and $cfile? 's':'') . ' skipped';
($rc = system("rm -f $htmpname $ctmpname"))
and warn "rm -f $htmpname $ctmpname failed, $rc";
$gerror = 1;
}
}
exit $gerror;
sub format_line {
my ($line, $maxlength, $break, $new_end, $new_start) = @_;
my $rline = "";
while ( length($line) > $maxlength
and ($i = rindex $line, $break, $maxlength-length($new_end)) != -1 ) {
$rline .= substr($line, 0, $i) . $new_end . "\n";
$line = $new_start . substr($line, $i+length($break));
}
return $rline . $line;
}
# This routine is a crude replacement for one in File::Basename. We
# cannot use any library code because it fouls up the Perl bootstrap
# when we update a perl version. MarkM
sub fileparse {
my ($filename, @suffix) = @_;
my ($dir, $name, $type, $i);
$type = '';
foreach $i (@suffix) {
if ($filename =~ m|$i$|) {
$filename =~ s|$i$||;
$type = $i;
}
}
if ($filename =~ m|/|) {
$filename =~ m|([^/]*)$|;
$name = $1;
$dir = $filename;
$dir =~ s|$name$||;
}
else {
$dir = '';
$name = $filename;
}
($name, $dir, $type);
}