#!/usr/bin/perl
#
# Copyright (c) 2007 Michael Taylor
# 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,
#    without modification.
# 2. Redistributions in binary form must reproduce at minimum a disclaimer
#    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
#    redistribution must be conditioned upon including a substantially
#    similar Disclaimer requirement for further binary redistribution.
# 3. Neither the names of the above-listed copyright holders nor the names
#    of any contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# Alternatively, this software may be distributed under the terms of the
# GNU General Public License ("GPL") version 2 as published by the Free
# Software Foundation.
#
# NO WARRANTY
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
#
# $Id: foo $
#
# This script is invoked at build time to regenerate a wrapper for the HAL
# binary API that adds locking and (optionally) tracing with human readable
# names.
#
use strict;
use warnings;

#
# This section defines the name translation from the binary HAL's function
# pointers to our API names.  Please provide all function names here.  If
# the wrapper is not needed, use undef.
#
my %wrapper_names = (
    "ah_beaconInit"               => "ath_hal_beaconinit",
    "ah_clrMulticastFilterIndex"  => "ath_hal_clearmcastfilter",
    "ah_detach"                   => undef,
    "ah_detectCardPresent"        => "ath_hal_detectcardpresent",
    "ah_disable"                  => "ath_hal_disable",
    "ah_disablePhyErrDiag"        => "ath_hal_disablePhyDiag",
    "ah_enablePhyErrDiag"         => "ath_hal_enablePhyDiag",
    "ah_enableReceive"            => "ath_hal_rxena",
    "ah_fillTxDesc"               => "ath_hal_filltxdesc",
    "ah_getAckCTSRate"            => "ath_hal_getackctsrate",
    "ah_getAckTimeout"            => "ath_hal_getacktimeout",
    "ah_getAntennaSwitch"         => "ath_hal_getantennaswitch",
    "ah_getBssIdMask"             => "ath_hal_getbssidmask",
    "ah_getCTSTimeout"            => "ath_hal_getctstimeout",
    "ah_getCapability"            => "ath_hal_getcapability",
    "ah_getChanNoise"             => "ath_hal_get_channel_noise",
    "ah_getDefAntenna"            => "ath_hal_getdefantenna",
    "ah_getDiagState"             => "ath_hal_getdiagstate",
    "ah_getInterrupts"            => "ath_hal_intrget",
    "ah_getKeyCacheSize"          => "ath_hal_keycachesize",
    "ah_getMacAddress"            => "ath_hal_getmac",
    "ah_getPendingInterrupts"     => "ath_hal_getisr",
    "ah_getPowerMode"             => "ath_hal_getPowerMode",
    "ah_getRateTable"             => "ath_hal_getratetable",
    "ah_getRfGain"                => "ath_hal_getrfgain",
    "ah_getRxDP"                  => "ath_hal_getrxbuf",
    "ah_getRxFilter"              => "ath_hal_getrxfilter",
    "ah_getSifsTime"              => "ath_hal_getsifstime",
    "ah_getSlotTime"              => "ath_hal_getslottime",
    "ah_getTsf32"                 => "ath_hal_gettsf32",
    "ah_getTsf64"                 => "ath_hal_gettsf64",
    "ah_getTxDP"                  => "ath_hal_gettxbuf",
    "ah_getTxIntrQueue"           => "ath_hal_gettxintrtxqs",
    "ah_getTxQueueProps"          => "ath_hal_gettxqueueprops",
    "ah_gpioCfgInput"             => "ath_hal_gpiCfgInput",
    "ah_gpioCfgOutput"            => "ath_hal_gpioCfgOutput",
    "ah_gpioGet"                  => "ath_hal_gpioget",
    "ah_gpioSet"                  => "ath_hal_gpioset",
    "ah_gpioSetIntr"              => "ath_hal_gpiosetintr",
    "ah_isInterruptPending"       => "ath_hal_intrpend",
    "ah_isKeyCacheEntryValid"     => "ath_hal_keyisvalid",
    "ah_numTxPending"             => "ath_hal_numtxpending",
    "ah_perCalibration"           => "ath_hal_calibrate",
    "ah_perCalibrationN"          => "ath_hal_calibrate11n",
    "ah_phyDisable"               => "ath_hal_phydisable",
    "ah_procMibEvent"             => "ath_hal_mibevent",
    "ah_procRxDesc"               => "ath_hal_rxprocdesc",
    "ah_procTxDesc"               => "ath_hal_txprocdesc",
    "ah_releaseTxQueue"           => "ath_hal_releasetxqueue",
    "ah_reqTxIntrDesc"            => "ath_hal_txreqintrdesc",
    "ah_reset"                    => "ath_hal_reset",
    "ah_resetCalValid"            => "ath_hal_resetcalvalid",
    "ah_resetKeyCacheEntry"       => "ath_hal_keyreset",
    "ah_resetStationBeaconTimers" => "ath_hal_beaconreset",
    "ah_resetTsf"                 => "ath_hal_resettsf",
    "ah_resetTxQueue"             => "ath_hal_resettxqueue",
    "ah_rxMonitor"                => "ath_hal_rxmonitor",
    "ah_setAckCTSRate"            => "ath_hal_setackctsrate",
    "ah_setAckTimeout"            => "ath_hal_setacktimeout",
    "ah_setAntennaSwitch"         => "ath_hal_setantennaswitch",
    "ah_setBeaconTimers"          => "ath_hal_setbeacontimers",
    "ah_setBssIdMask"             => "ath_hal_setbssidmask",
    "ah_setCTSTimeout"            => "ath_hal_setctstimeout",
    "ah_setCapability"            => "ath_hal_setcapability",
    "ah_setChannel"               => "ath_hal_setchannel",
    "ah_setCoverageClass"         => "ath_hal_setcoverageclass",
    "ah_setDecompMask"            => "ath_hal_setdecompmask",
    "ah_setDefAntenna"            => "ath_hal_setdefantenna",
    "ah_setInterrupts"            => "ath_hal_intrset",
    "ah_setKeyCacheEntry"         => "ath_hal_keyset",
    "ah_setKeyCacheEntryMac"      => "ath_hal_keysetmac",
    "ah_setLedState"              => "ath_hal_setledstate",
    "ah_setMacAddress"            => "ath_hal_setmac",
    "ah_setMulticastFilter"       => "ath_hal_setmcastfilter",
    "ah_setMulticastFilterIndex"  => "ath_hal_setmcastfilterindex",
    "ah_setPCUConfig"             => "ath_hal_setopmode",
    "ah_setPowerMode"             => "ath_hal_setpower",
    "ah_setRegulatoryDomain"      => "ath_hal_setregulatorydomain",
    "ah_setRxDP"                  => "ath_hal_putrxbuf",
    "ah_setRxFilter"              => "ath_hal_setrxfilter",
    "ah_setSifsTime"              => "ath_hal_setsifstime",
    "ah_setSlotTime"              => "ath_hal_setslottime",
    "ah_setStationBeaconTimers"   => "ath_hal_beacontimers",
    "ah_setTxDP"                  => "ath_hal_puttxbuf",
    "ah_setTxPowerLimit"          => "ath_hal_settxpowlimit",
    "ah_setTxQueueProps"          => "ath_hal_settxqueueprops",
    "ah_setupRxDesc"              => "ath_hal_setuprxdesc",
    "ah_setupTxDesc"              => "ath_hal_setuptxdesc",
    "ah_setupTxQueue"             => "ath_hal_setuptxqueue",
    "ah_setupXTxDesc"             => "ath_hal_setupxtxdesc",
    "ah_startPcuReceive"          => "ath_hal_startpcurecv",
    "ah_startTxDma"               => "ath_hal_txstart",
    "ah_stopDmaReceive"           => "ath_hal_stopdmarecv",
    "ah_stopPcuReceive"           => "ath_hal_stoppcurecv",
    "ah_stopTxDma"                => "ath_hal_stoptxdma",
    "ah_updateCTSForBursting"     => "ath_hal_updateCTSForBursting",
    "ah_updateMibCounters"        => "ath_hal_updatemibcounters",
    "ah_updateTxTrigLevel"        => "ath_hal_updatetxtriglevel",
    "ah_waitForBeaconDone"        => "ath_hal_waitforbeacon",
    "ah_writeAssocid"             => "ath_hal_setassocid"
);

#
# This text is copied verbatim to the top of the output header file
#
my $header = <<EOF
/*-
 * Copyright (c) 2007 Michael Taylor
 * 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,
	without modification.
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
 *    redistribution must be conditioned upon including a substantially
 *    similar Disclaimer requirement for further binary redistribution.
 * 3. Neither the names of the above-listed copyright holders nor the names
 *    of any contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 *
 * NO WARRANTY
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
 */
/* **************************************************************
 * *   WARNING: THIS IS A GENERATED FILE.  PLEASE DO NOT EDIT   *
 * ************************************************************** */

#include "if_ath_hal_macros.h"
#ifdef CONFIG_KALLSYMS
#include "linux/kallsyms.h"
#endif /* CONFIG_KALLSYMS */

#ifndef _IF_ATH_HAL_H_
#define _IF_ATH_HAL_H_
EOF
  ;

#
# This text is copied verbatim to the bottom of the output header file
#
my $footer = <<EOF

#include "if_ath_hal_wrappers.h"
#include "if_ath_hal_extensions.h"

#endif				/* #ifndef _IF_ATH_HAL_H_ */
 /* *** THIS IS A GENERATED FILE -- DO NOT EDIT *** */
 /* *** THIS IS A GENERATED FILE -- DO NOT EDIT *** */
 /* *** THIS IS A GENERATED FILE -- DO NOT EDIT *** */
EOF
  ;

# Parsed Function Data

# hash of string->string (hal's function name to return type)
my %return_types = ();

# hash of string->list of strings (ordered list of parameter names)
my %parameter_names = ();

# hash of string->list of strings (ordered list of parameter types)
my %parameter_types = ();

# Pick apart the return type, parameter types, and parameter names for each HAL function
sub parse_prototype($) {
    my $proto = shift @_;
    $proto =~
/^((?:(?:const|struct)\s*)*[^\s]+(?:[\s]*\*)?)[\s]*__ahdecl\(\*([^\)]*)\)\((.*)\);/;
    my $return_type   = $1;
    my $member_name   = $2;
    my $parameterlist = $3;

    my $api_name = $wrapper_names{$member_name};
    if ( exists $wrapper_names{$member_name} ) {
        if ( !defined $api_name ) {
            return;    # known name, but no wrapper needed
        }
    }
    else {
        print STDERR "No wrapper name for $member_name\n";
        exit 1;
    }

    $return_types{"$member_name"} = $return_type;
    @{ $parameter_names{"$member_name"} } = ();
    @{ $parameter_types{"$member_name"} } = ();
    my @parameters = split /,\s?/, $parameterlist;
    my $argnum = 0;

    foreach (@parameters) {
        s/ \*/\* /;
        /^((?:(?:const|struct|\*)\s*)*)([^\s]+\*?)\s*([^\s]*)\s*/;
        my $type = "$1$2";
        my $name = "$3";
        if ( 0 == length($name) ) {
            if ( $argnum == 0 && $type =~ /ath_hal/ ) {
                $name = "ah";
            }
            else {
                $name = "a$argnum";
            }
        }

        push @{ $parameter_names{$member_name} }, $name;
        push @{ $parameter_types{$member_name} }, $type;
        $argnum++;
    }
}

# Parse and scrub the hal structure's member function declarations
sub parse_input() {
    my $line_continued = 0;
    my $line_buffer    = "";
    foreach (<INPUT>) {
        chomp($_);
        s/\s+$//g;
        s/^\s+//g;
        s/\s+/ /g;
        if ( /__ahdecl\s*\(.*/ || $line_continued ) {
            $line_buffer .= "$_";
            if ( /__ahdecl.*;/ || ( $line_continued && /;/ ) ) {
                parse_prototype($line_buffer);
                $line_buffer    = "";
                $line_continued = 0;
            }
            else {
                $line_buffer .= " ";
                $line_continued = 1;
            }
        }
    }
}

# Arrange spaces in the type name nicely
sub format_type($) {
    my $type = shift @_;
    if ( $type =~ /\*$/ ) {
        $type =~ s/^([^*]*[^*\s])\s*(\*+)$/$1 $2/;
    }
    else {
        $type .= " ";
    }
    return $type;
}

# Generate the header file
sub generate_output() {
    print OUTPUT $header;

    for my $member_name ( keys %return_types ) {
        my $api_name        = $wrapper_names{$member_name};
        my $api_return_type = $return_types{$member_name};
        my $ret_void        = ( $api_return_type =~ /void/ );
        print OUTPUT "\nstatic inline "
          . format_type($api_return_type)
          . "$api_name(";
        my @names = @{ $parameter_names{$member_name} };
        my @types = @{ $parameter_types{$member_name} };
        for my $i ( 0 .. $#names ) {
            if ($i) {
                print OUTPUT ", ";
            }
            print OUTPUT format_type( $types[$i] ) . $names[$i];
        }
        print OUTPUT ")\n{";
        if ( !$ret_void ) {
            print OUTPUT "\n\t" . format_type($api_return_type) . "ret;";
        }
        print OUTPUT "\n\tATH_HAL_LOCK_IRQ(ah->ah_sc);";
        print OUTPUT "\n\tath_hal_set_function(__func__);";
        print OUTPUT "\n\tath_hal_set_device(SC_DEV_NAME(ah->ah_sc));";
        print OUTPUT "\n\t";
        if ( !$ret_void ) {
            print OUTPUT "ret = ";
        }

        print OUTPUT "ah->$member_name(";
        for my $j ( 0 .. $#names ) {
            if ($j) {
                print OUTPUT ", ";
            }
            print OUTPUT $names[$j];
        }
        print OUTPUT ");";
        print OUTPUT "\n\tath_hal_set_function(NULL);";
        print OUTPUT "\n\tath_hal_set_device(NULL);";
        print OUTPUT "\n\tATH_HAL_UNLOCK_IRQ(ah->ah_sc);";
        if ( !$ret_void ) {
            print OUTPUT "\n\treturn ret;";
        }
        print OUTPUT "\n}\n";
    }
    print OUTPUT "\n/* Example script to create a HAL function unmangling SED script: ";
    print OUTPUT "\n";
    print OUTPUT "\n   dmesg -c &>/dev/null && iwpriv ath0 dump_hal_map && dmesg | \\";
    print OUTPUT "\n           sed -n -r -e \"/zz[0-9a-f]{8}/ { s~^([^+]*)[^=]*=(.*)~s/\\1\\\/\\2 (\\1)/g~; p; } \" \\";
    print OUTPUT "\n           >hal_unmangle.sed";
    print OUTPUT "\n";
    print OUTPUT "\n * Example usage:";
    print OUTPUT "\n";
    print OUTPUT "\n           tail -f /var/log/messages | sed -f hal_unmangle.sed ";
    print OUTPUT "\n */";
    print OUTPUT "\nstatic inline void ath_hal_dump_map(struct ath_hal* ah) {";
    print OUTPUT "\n#ifdef CONFIG_KALLSYMS\n";
    for my $member_name ( keys %return_types ) {
	my $api_name        = $member_name;
	my $api_return_type = $return_types{$member_name};
	my $ret_void        = ( $api_return_type =~ /void/ );
	print OUTPUT "\n\t/* "
	  . format_type($api_return_type)
	  . "$api_name(";
	my @names = @{ $parameter_names{$member_name} };
	my @types = @{ $parameter_types{$member_name} };
	for my $i ( 0 .. $#names ) {
	    if ($i) {
		print OUTPUT ", ";
	    }
	    print OUTPUT format_type( $types[$i] ) . $names[$i];
	}
	print OUTPUT ") */";
	print OUTPUT "\n\t__print_symbol(\"%s=" . $member_name 
	   . "\\n\", (unsigned long)ah->"
	   . $member_name . ");";
    }
    print OUTPUT "\n#else /* #ifdef CONFIG_KALLSYMS */\n";
    print OUTPUT "\nprintk(\"To use this feature you must enable "
	       . "CONFIG_KALLSYMS in your kernel.\");\n";
    print OUTPUT "\n#endif /* #ifndef CONFIG_KALLSYMS */\n";
    print OUTPUT "\n}\n";
    print OUTPUT $footer;
}

sub main () {

    # Get input and output files from the arguments
    if ( $#ARGV != 1 ) {
        print STDERR "Need two arguments: input and output\n";
        exit 1;
    }

    my $input_header  = $ARGV[0];
    my $output_header = $ARGV[1];

    if ( !open INPUT, "<$input_header" ) {
        die "Cannot open \"$input_header\": $!";
    }
    parse_input();
    close INPUT;

    if ( !open OUTPUT, ">$output_header" ) {
        close INPUT;
        die "Cannot open \"$output_header\": $!";
    }
    generate_output();
    close OUTPUT;
}

main();
