#!/usr/bin/perl
#     (c)2007-2011 Broadcom Corporation
#
#  This program is the proprietary software of Broadcom Corporation and/or its licensors,
#  and may only be used, duplicated, modified or distributed pursuant to the terms and
#  conditions of a separate, written license agreement executed between you and Broadcom
#  (an "Authorized License").  Except as set forth in an Authorized License, Broadcom grants
#  no license (express or implied), right to use, or waiver of any kind with respect to the
#  Software, and Broadcom expressly reserves all rights in and to the Software and all
#  intellectual property rights therein.  IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU
#  HAVE NO RIGHT TO USE THIS SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY
#  NOTIFY BROADCOM AND DISCONTINUE ALL USE OF THE SOFTWARE.
#
#  Except as expressly set forth in the Authorized License,
#
#  1.     This program, including its structure, sequence and organization, constitutes the valuable trade
#  secrets of Broadcom, and you shall use all reasonable efforts to protect the confidentiality thereof,
#  and to use this information only in connection with your use of Broadcom integrated circuit products.
#
#  2.     TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
#  AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES, REPRESENTATIONS OR
#  WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
#  THE SOFTWARE.  BROADCOM SPECIFICALLY DISCLAIMS ANY AND ALL IMPLIED WARRANTIES
#  OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE,
#  LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION
#  OR CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING OUT OF
#  USE OR PERFORMANCE OF THE SOFTWARE.
#
#  3.     TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM OR ITS
#  LICENSORS BE LIABLE FOR (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR
#  EXEMPLARY DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO YOUR
#  USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM HAS BEEN ADVISED OF
#  THE POSSIBILITY OF SUCH DAMAGES; OR (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT
#  ACTUALLY PAID FOR THE SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
#  LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF
#  ANY LIMITED REMEDY.
#
# $brcm_Workfile: bapi_thunks.pm $
# $brcm_Revision: 17 $
# $brcm_Date: 12/7/11 10:41a $
#
# File Description:
#
# Revision History:
#
# $brcm_Log: /nexus/build/tools/syncthunk/bapi_thunks.pm $
# 
# 17   12/7/11 10:41a erickson
# SW7420-2141: merge as much duplicated kernelmode proxy/usermode ipc
#  perl code as possible
# 
# 16   10/31/11 6:13p bandrews
# SW7231-391: casts to support parser band and timebase as enums
# 
# SW7420-2078/1   10/11/11 8:22p bandrews
# SW7231-391: added casts to support dual variant usage of parser band
#  and timebase
# 
# 15   9/2/11 10:03a erickson
# SW7420-1148: allow nexus_register/unregister to objdb from nexus
#  module. enables more complex resource management.
# 
# 14   7/13/11 3:43p erickson
# SW7420-1979: #include nexus_class_verification.h is always required
#  because of automatic shutdown
# 
# 13   7/12/11 1:27p erickson
# SW7420-1979: add support for new shutdown attribute
# 
# 12   3/17/11 12:00p erickson
# SW7346-101: fix NEXUS_StopCallbacks for secondary destructors
#  (specifically, NEXUS_Playpump_ClosePidChannel)
#
# 11   1/27/11 3:05p erickson
# SW7208-152: fix and standardize get_destructor_hash
#
# 10   1/27/11 1:43p erickson
# SW7208-152: fix generation of NEXUS_StopCallbacks
#
# 9   1/19/11 2:02p erickson
# SW7420-1123: socket-based usermode IPC
#
# 8   9/22/09 1:54p gmohile
# SW7405-3060 : Export kernel symbols
#
# 7   3/28/08 10:47a vsilyaev
# PR 40863: Call NEXUS_StopCalbacks prior to the 'destructor' call, since
#  StopCallback is sticky it would prevent further callback to fire
#
# 6   3/24/08 6:58p vsilyaev
# PR 40863: Changed order of calls to NEXUS_StopCallbacks and
#  "destructors" in ordder to stop callbacks that were fired during call
#  to the destructor function
#
# 5   3/24/08 6:42p vsilyaev
# PR 40863: Added injection of NEXUS_StopCallbacks into all destructors
#
# 4   3/21/08 11:57a vsilyaev
# PR 38682: Fixed typo in description
#
# 3   1/25/08 4:24p vsilyaev
# PR 38682: Reduce code size of the generated code
#
# 2   1/24/08 4:34p vsilyaev
# PR 38682: Reduced size and made optional tracing of thunk layer
#
# 1   1/18/08 2:15p jgarrett
# PR 38808: Merging to main branch
#
# Nexus_Devel/5   10/15/07 2:53p vsilyaev
# PR 35824: Fixed handling of (void) functions
#
# Nexus_Devel/4   10/12/07 11:49a erickson
# PR36066: added "or die" to file opens
#
# Nexus_Devel/3   10/8/07 3:21p erickson
# PR35395: added params and retval to trace
#
# Nexus_Devel/2   10/3/07 12:14p erickson
# PR35395: enable thunk trace
#
# Nexus_Devel/1   9/27/07 1:33p erickson
# PR35395: initial impl
#
#############################################################################
use strict;

package bapi_thunks;

use bapi_common;
use bapi_classes;

# for user-mode IPC, server-side does not pass through the IPC thunk. Therefore a minimal amount of
# hook is needed in the one thunk that is passed: the sync thunk.
sub generate_ipc_code
{
    my ($func, $destructors, $classes) = @_;
    my @server_pre_lock;
    my @server_post_success;
    my $class;

    CLASS: for $class (@$classes) {
        if ($class->{DESTRUCTOR} == $func) {
            for (@{$class->{SHUTDOWN}}) {
                my $shutdown_target = $_->{'shutdown_target'};
                my $shutdown_get_connector = $_->{'shutdown_get_connector'};
                my $params = $func->{PARAMS};
                my $handle = $$params[0]->{NAME};
                push @server_pre_lock, "nexus_driver_shutdown_$shutdown_target($shutdown_get_connector($handle));";
            }
        }
    }
    
    my $stopcallbacks_handle = bapi_classes::get_stopcallbacks_handle $func, $destructors;
    if (defined $stopcallbacks_handle) {
        push @server_pre_lock, "NEXUS_StopCallbacks((void*)$stopcallbacks_handle);";
    }
    
    my %result;
    $result{'server_pre_lock'} = \@server_pre_lock;
    $result{'server_post_success'} = \@server_post_success;
    return \%result;
}

sub build_thunks
{
    my ($module, $filename, $funcs) = @_;
    my $func;
    my $destructors = bapi_classes::get_destructors $funcs;
    my $classes = bapi_classes::get_classes $funcs, $destructors;

    open FILE, ">$filename" or die;
    print FILE "/*********************************\n";
    print FILE "*\n";
    print FILE "* This file is autogenerated by the Nexus Platform makefile.\n";
    print FILE "*\n";
    print FILE "* This file acquires the module lock for every public API call into that module. The actual implementation of each function is remapped to _impl.\n";
    print FILE "*\n";
    print FILE "*********************************/\n";
    print FILE "#define NEXUS_THUNK_LAYER\n";
    print FILE "#include \"nexus_${module}_module.h\"\n";
    print FILE "#include \"nexus_core_utils.h\"\n";
    print FILE "#include \"nexus_class_verification.h\"\n";
    print FILE "BDBG_MODULE(nexus_${module}_thunks);\n";
    print FILE "#define BDBG_MSG_TRACE(x) \n";
    print FILE "/* use local static function in order to reduce size of compiled code, size would reduce since NEXUS_XXXModule expanded to function call with three arguments (where at least one is a global symbol) */\n";
    print FILE "static void module_lock(void) { NEXUS_LockModule();}\n";
    print FILE "static void module_unlock(void) { NEXUS_UnlockModule();}\n";


    # generate prototypes for all the _impl functions
    for $func (@{$funcs}) {
        # NOTE: If there's an exception and a function does not belong in the thunk layer, add it here.

        my $impl = $func->{PROTOTYPE};
        $impl =~ s/$func->{FUNCNAME}/$func->{FUNCNAME}_impl/;
        print FILE "$impl;\n";
    }

    # generate the actual thunk layer functions which call the impl functions
    for $func (@{$funcs}) {
        my $params = $func->{PARAMS};
        my $param;
        my $generated_code = generate_ipc_code $func, $destructors, $classes;

        print FILE "$func->{PROTOTYPE}\n\{\n";
        if ($func->{RETTYPE} eq "void") {
        }
        else {
            print FILE "  $func->{RETTYPE} result;\n";
        }

        # enter MSG
        print FILE "  BDBG_MSG_TRACE((\">%s\(";
        for $param (@$params) {
            print FILE "%#lx";
            if ($param != $params->[-1]) { print FILE ", "; }
        }
        print FILE ")\" ";
        print FILE ", \"$func->{FUNCNAME}\"";
        for $param (@$params) {
            print FILE ", ";
            print FILE "(unsigned long)$param->{NAME}";
        }
        print FILE "));\n";

        if (scalar @{$generated_code->{'server_pre_lock'}}) {
            my $file = \*FILE;
            bapi_util::print_code $file, $generated_code->{'server_pre_lock'}, "  ";
        }

        # make call
        print FILE "  module_lock();\n";

        if ($func->{RETTYPE} eq "void") {
            print FILE "  $func->{FUNCNAME}_impl(";
        }
        else {
            print FILE "  result = $func->{FUNCNAME}_impl(";
        }
        for $param (@$params) {
            print FILE "$param->{NAME}";
            if ($param != $params->[-1]) { print FILE ", "; }
        }
        print FILE ");\n";

        # leave MSG and return value
        if ($func->{RETTYPE} eq "void") {
            print FILE "  module_unlock();\n";
            print FILE "  BDBG_MSG_TRACE((\"<%s\", \"$func->{FUNCNAME}\"));\n";
            print FILE "  return;\n";
        }
        else {
            print FILE "  module_unlock();\n";
            if (scalar @{$generated_code->{'server_post_success'}}) {
                if ($func->{RETTYPE} eq "NEXUS_Error") {
                    print FILE "  if (!result) {\n";
                }
                else {
                    print FILE "  if (result) {\n";
                }
                my $file = \*FILE;
                bapi_util::print_code $file, $generated_code->{'server_post_success'}, "    ";
                print FILE "  }\n";
            }
            print FILE "  BDBG_MSG_TRACE((\"<%s\=%#lx\", \"$func->{FUNCNAME}\", (unsigned long)result));\n";
            print FILE "  return result;\n";
        }
        print FILE "}\n\n";
    }
    close FILE;
}

sub build_remapping
{
    my ($filename, $funcs) = @_;
    my $func;
    open FILE, ">$filename" or die;

    print FILE "/*********************************\n";
    print FILE "*\n";
    print FILE "* This file is autogenerated by the Nexus Platform makefile.\n";
    print FILE "*\n";
    print FILE "* This file remaps every public API function to _impl. This allows the Nexus modules to call their own public API without reacquiring the module lock.\n";
    print FILE "*\n";
    print FILE "*********************************/\n";
    print FILE "#ifndef NEXUS_THUNK_LAYER\n";

    for $func (@{$funcs}) {
        print FILE "#define $func->{FUNCNAME} $func->{FUNCNAME}_impl\n";
    }
    print FILE "#endif\n";
    print FILE "\n";
    close FILE;
}


1;
