meta/parse.pl (3,897 lines of code) (raw):

#!/usr/bin/perl # # Copyright (c) 2014 Microsoft Open Technologies, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 # # THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR # CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT # LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS # FOR A PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT. # # See the Apache Version 2.0 License for specific language governing # permissions and limitations under the License. # # Microsoft would like to thank the following companies for their review and # assistance with these files: Intel Corporation, Mellanox Technologies Ltd, # Dell Products, L.P., Facebook, Inc., Marvell International Ltd. # # @file parse.pl # # @brief This module defines SAI Metadata Parser # BEGIN { push @INC,'.'; } use strict; use warnings; use diagnostics; use sort 'stable'; # for enum initializers sort #use XML::Simple qw(:strict); use Getopt::Std; use Data::Dumper; use utils; use xmlutils; use style; use test; use serialize; use cap; our $XMLDIR = "xml"; our $INCLUDE_DIR = "../inc/"; our $EXPERIMENTAL_DIR = "../experimental/"; our $MAX_CONDITIONS_LEN = 1; our %SAI_ENUMS = (); our %SAI_UNIONS = (); our %METADATA = (); our %NON_OBJECT_ID_STRUCTS = (); our %NOTIFICATIONS = (); our %OBJTOAPIMAP = (); our %APITOOBJMAP = (); our %ALL_STRUCTS = (); our %OBJECT_TYPE_MAP = (); our %SAI_DEFINES = (); our %EXTRA_RANGE_DEFINES = (); our %REVGRAPH = (); our %EXTENSIONS_ENUMS = (); our %EXTENSIONS_ATTRS = (); our %EXPERIMENTAL_OBJECTS = (); our %OBJECT_TYPE_TO_STATS_MAP = (); our %ATTR_TO_CALLBACK = (); our %PRIMITIVE_TYPES = (); our %FUNCTION_DEF = (); our @ALL_ENUMS = (); our %GLOBAL_APIS = (); our %OBJECT_TYPE_BULK_MAP = (); our %SAI_ENUMS_CUSTOM_RANGES = (); my $FLAGS = "MANDATORY_ON_CREATE|CREATE_ONLY|CREATE_AND_SET|READ_ONLY|KEY"; my $ENUM_FLAGS_TYPES = "(none|strict|mixed|ranges|free)"; # TAGS HANDLERS my %ATTR_TAGS = ( "type" , \&ProcessTagType, "flags" , \&ProcessTagFlags, "objects" , \&ProcessTagObjects, "allownull" , \&ProcessTagAllowNull, "allowempty" , \&ProcessTagAllowEmpty, "condition" , \&ProcessTagCondition, "validonly" , \&ProcessTagCondition, # since validonly uses same format as condition "default" , \&ProcessTagDefault, "ignore" , \&ProcessTagIgnore, "isvlan" , \&ProcessTagIsVlan, "getsave" , \&ProcessTagGetSave, "range" , \&ProcessTagRange, "relaxed" , \&ProcessTagRelaxed, "isresourcetype" , \&ProcessTagIsRecourceType, "deprecated" , \&ProcessTagDeprecated, ); my %options = (); getopts("dsASl", \%options); our $optionPrintDebug = 1 if defined $options{d}; our $optionDisableAspell = 1 if defined $options{A}; our $optionUseXmlSimple = 1 if defined $options{s}; our $optionDisableStyleCheck = 1 if defined $options{S}; our $optionShowLogCaller = 1 if defined $options{l}; # LOGGING FUNCTIONS HELPERS $SIG{__DIE__} = sub { LogError "FATAL ERROR === MUST FIX === : @_"; exit 1; }; my %ACL_FIELD_TYPES = (); my %ACL_FIELD_TYPES_TO_VT = (); my %ACL_ACTION_TYPES = (); my %ACL_ACTION_TYPES_TO_VT = (); my %VALUE_TYPES = (); my %VALUE_TYPES_TO_VT = (); my %CAPABILITIES = (); sub ProcessTagType { my ($type, $value, $val) = @_; return $val if $val =~ /^sai_s32_list_t sai_\w+_t$/; return $val if $val =~ /^sai_acl_field_data_t (sai_\w+_t|bool)$/; return $val if $val =~ /^sai_acl_field_data_mask_t (sai_\w+_t|bool)$/; return $val if $val =~ /^sai_acl_action_data_t (sai_\w+_t|bool)$/; return $val if $val =~ /^(bool|char)$/; return $val if $val =~ /^sai_\w+_t$/ and not $val =~ /_attr_(extensions_)?t/; return $val if $val =~ /^sai_pointer_t sai_\w+_notification_fn$/; if ($val =~ /^sai_pointer_t (sai_switch_\w+_fn)$/) { $ATTR_TO_CALLBACK{$value} = $1; return $val; } LogError "invalid type tag value '$val' expected sai type or enum"; return undef; } sub ProcessTagFlags { my ($type, $value, $val) = @_; $val =~ s/\s*//g; my @flags = split/\|/,$val; for my $flag (@flags) { if (not $flag =~ /^($FLAGS)$/) { LogError "invalid flags tag value '$val' ($flag) on $value, expected in '$FLAGS'"; return undef; } } return \@flags; } sub ProcessTagObjects { my ($type, $value, $val) = @_; $val =~ s/\s*//g; my @ots = split/[,]/,$val; for my $ot (@ots) { if (not $ot =~ /^SAI_OBJECT_TYPE_\w+$/) { LogError "invalid objecttype tag value '$val' ($ot) in $type $value"; return undef; } } return \@ots; } sub ProcessTagAllowNull { my ($type, $value, $val) = @_; return $val if $val =~ /^(true|false)$/; LogError "allownull tag value '$val', expected true/false"; return undef; } sub ProcessTagAllowEmpty { my ($type, $value, $val) = @_; return $val if $val =~ /^(true|false)$/; LogError "allowempty tag value '$val', expected true/false"; return undef; } sub ProcessMixedConditionTag { my ($type, $value, $val) = @_; LogDebug "Processing mix condition: '$val'"; $val = "($val)" if not $val =~ /^\(/; my $COND_OP = '(?:==|!=|<=|>=|<|>)'; my @parts = split/(SAI_\w+\s*$COND_OP\s*(?:true|false|SAI_\w+|$NUMBER_REGEX))/,$val; my $short = ""; my @conds = (); for my $part (@parts) { LogDebug "'$part'"; if ($part =~ /(SAI_\w+\s*$COND_OP\s*(?:true|false|SAI_\w+|$NUMBER_REGEX))/) { $short .= "C"; push@conds,$part; next; } $part =~ s/\band\b/a/g; $part =~ s/\bor\b/o/g; $short .= $part; } $short =~ s/\s+//g; LogDebug "$short"; my $RPN = ""; my @stack = (); my @conditions = (); if ($short =~ /CC|[ao][ao]/) { LogError "wrong condition, missing and/or: ($short): $val"; return undef; } while ($short =~ /(.)/g) { LogDebug "short = $1"; my $c = $1; next if ($c eq "("); if ($c eq "C") { $RPN .= "C"; my $cond = shift @conds; push@conditions,$cond; } elsif ($c eq ')') { my $o = pop@stack; if (not defined $o) { LogError "not pairded ')' in $short: $val"; return undef; } $RPN .= $o; push@conditions,"SAI_ATTR_CONDITION_TYPE_AND" if $o eq "a"; push@conditions,"SAI_ATTR_CONDITION_TYPE_OR" if $o eq "o"; } elsif ($c eq "a" or $c eq "o") { push@stack,$c; } else { LogError "unsupported char $c in $short: $val"; return undef; } } my $len = @stack; if ($len != 0) { LogError "wrong stack length: $len (@stack) (short: '$short') unpaired/missing brackets () on condition: $val"; return undef; } # $RPN = reverse $RPN; LogDebug "RPN = $RPN | @conditions"; unshift @conditions, "SAI_ATTR_CONDITION_TYPE_MIXED"; return \@conditions; } sub ProcessTagCondition { my ($type, $value, $val) = @_; my @conditions = split/\s+(?:or|and)\s+/,$val; if ($val =~ /or.+and|and.+or/) { return ProcessMixedConditionTag($type, $value, $val); } for my $cond (@conditions) { if (not $cond =~ /^(SAI_\w+) == (true|false|SAI_\w+|$NUMBER_REGEX)$/) { LogError "invalid condition tag value '$val' ($cond), expected SAI_ENUM == true|false|SAI_ENUM|number"; return undef; } } # if there is only one condition, then type does not matter $type = "SAI_ATTR_CONDITION_TYPE_" . (($val =~ /and/) ? "AND" : "OR"); unshift @conditions, $type; return \@conditions; } sub ProcessTagDefault { my ($type, $value, $val) = @_; return $val if $val =~ /^(empty|internal|vendor|const)/; return $val if $val =~ /^(attrvalue) SAI_\w+_ATTR_\w+$/; return $val if $val =~ /^(true|false|NULL|SAI_\w+|$NUMBER_REGEX)$/ and not $val =~ /_ATTR_|OBJECT_TYPE/; return $val if $val =~ /^0\.0\.0\.0$/; return $val if $val =~ /^00:00:00:00:00:00$/; return $val if $val eq "disabled"; return $val if $val eq "\"\""; return $val if $val =~ /^ffff\:ffff\:ffff\:ffff\:ffff\:ffff\:ffff\:ffff$/; LogError "invalid default tag value '$val' on $type $value"; return undef; } sub ProcessTagIgnore { # just return true if tag is defined return "true"; } sub ProcessTagIsVlan { my ($type, $value, $val) = @_; return $val if $val =~ /^(true|false)$/i; LogError "isvlan tag value '$val', expected true/false"; return undef; } sub ProcessTagRelaxed { my ($type, $value, $val) = @_; return $val if $val =~ /^(true|false)$/i; LogError "relaxed tag value '$val', expected true/false"; return undef; } sub ProcessTagIsRecourceType { my ($type, $value, $val) = @_; return $val if $val =~ /^(true|false)$/i; LogError "isresourcetype tag value '$val', expected true/false"; return undef; } sub ProcessTagDeprecated { my ($type, $value, $val) = @_; return $val if $val =~ /^(true|false)$/i; LogError "deprecated tag value '$val', expected true/false"; return undef; } sub ProcessTagRange { my ($type, $attrName, $value) = @_; $value = Trim $value; if (not $value =~ /^SAI_\w+$/) { LogWarning "invalid range value: '$value', expected 'SAI_\\w+'"; return "-1" } LogInfo "Creating range attrs $attrName .. MAX"; my $range = $SAI_DEFINES{$value}; if (not defined $range or not $range =~ /^$NUMBER_REGEX$/) { LogWarning "range '$value' is not defined or not numer"; return "-1"; } $range = hex $range; if ($range < 0 or $range > 0x100) { LogWarning "range $value $range is negative or > 0x100"; next; } return $range; } sub ProcessEnumItemDescription { my ($type, $value, $desc) = @_; my @order = (); $desc =~ s/@@/\n@@/g; while ($desc =~ /@@(\w+)(.*)/g) { my $tag = $1; my $val = $2; push @order,$tag; $val = Trim $val; next if $tag eq "ignore"; LogError "tag '$tag' is not supported on enum ${type}::$value: $val"; } } sub ProcessDescription { my ($type, $value, $desc, $brief) = @_; my @order = (); $desc =~ s/@@/\n@@/g; while ($desc =~ /@@(\w+)(.*)/g) { my $tag = $1; my $val = $2; push @order,$tag; $val = Trim $val; if (not defined $ATTR_TAGS{$tag}) { LogError "unrecognized tag '$tag' on $type $value"; next; } $val = $ATTR_TAGS{$tag}->($type, $value, $val); $METADATA{$type}{$value}{$tag} = $val; $METADATA{$type}{$value}{objecttype} = $type; $METADATA{$type}{$value}{attrid} = $value; } $brief = Trim $brief; $METADATA{$type}{$value}{brief} = $brief if $brief ne ""; return if scalar@order == 0; my $rightOrder = 'type:flags(:objects)?(:allownull)?(:allowempty)?(:isvlan)?(:default)?(:range)?(:condition|:validonly)?(:relaxed)?(:isresourcetype)?(:deprecated)?'; my $order = join(":",@order); return if $order =~ /^$rightOrder$/; return if $order =~ /^ignore$/; LogWarning "metadata tags are not in right order: $order on $value"; LogWarning " correct order: $rightOrder or ignore"; } sub ProcessDefineSection { my $section = shift; for my $memberdef (@{ $section->{memberdef} }) { next if not $memberdef->{kind} eq "define"; my $name = $memberdef->{name}[0]; next if (not $name =~ /^SAI_\w+$/); my $initializer = $memberdef->{initializer}[0]; next if (not $initializer =~ /^(\(?".*"|$NUMBER_REGEX\)?)$/); $SAI_DEFINES{$name} = $1; LogDebug "adding define $name = $initializer"; } } sub ProcessEnumSection { my $section = shift; for my $memberdef (@{ $section->{memberdef} }) { next if not $memberdef->{kind} eq "enum"; my $enumtypename = $memberdef->{name}[0]; $enumtypename =~ s/^_//; if (not $enumtypename =~ /^(sai_\w+_)t$/) { LogWarning "enum $enumtypename is not prefixed sai_"; next; } my $enumprefix = uc $1; if ($enumtypename =~ /_extensions_t$/) { LogDebug "removing extension prefix from $enumtypename"; # remove extensions suffix on all extensions since they will be merged together $enumprefix =~ s/EXTENSIONS_$//; $EXTENSIONS_ENUMS{$enumtypename} = "${enumprefix}_t"; } if (defined $SAI_ENUMS{$enumtypename}) { LogError "duplicated enum $enumtypename"; next; } my $ed = ExtractDescription($enumtypename, $enumtypename, $memberdef->{detaileddescription}[0]); if ($ed =~ /\@\@flags/s and not $ed =~ /\@\@flags\s+(\w+)/s) { LogWarning "expected flags type $ENUM_FLAGS_TYPES not specified on $enumtypename"; } $SAI_ENUMS{$enumtypename}{flagsenum} = ($ed =~ /\@\@flags/s) ? "true" : "false"; $SAI_ENUMS{$enumtypename}{flagstype} = ($ed =~ /\@\@flags\s+(\w+)/s) ? $1 : "none"; my @arr = (); my @initializers = (); $SAI_ENUMS{$enumtypename}{values} = \@arr; for my $ev (@{ $memberdef->{enumvalue} }) { my $enumvaluename = $ev->{name}[0]; my $eitemd = ExtractDescription($enumtypename, $enumvaluename, $ev->{detaileddescription}[0]); my $initializer = $ev->{initializer}[0]; $initializer = "" if not defined $initializer; if ($eitemd =~ /\@ignore/) { LogInfo "Ignoring $enumvaluename"; if ($initializer =~ /^= (SAI_\w+)$/) { LogError "initializer $1 not defined in $enumtypename before $enumvaluename" if not grep (/^$1$/, @arr); } else { LogWarning "Enum $enumvaluename is ignored, but initializer is '$initializer' not in form '= SAI_\\w+'"; } # process ignore attributes if (not defined $SAI_ENUMS{$enumtypename}{ignoreval}) { my @ignoreval = (); $SAI_ENUMS{$enumtypename}{ignoreval} = \@ignoreval; } my $ref = $SAI_ENUMS{$enumtypename}{ignoreval}; push @$ref, $enumvaluename; next; } LogDebug "$enumtypename $enumvaluename"; push@arr,$enumvaluename; push@initializers,$initializer; LogWarning "Value $enumvaluename of $enumtypename is not prefixed as $enumprefix" if not $enumvaluename =~ /^$enumprefix/; if (not $enumvaluename =~ /^[A-Z0-9_]+$/) { LogError "enum $enumvaluename uses characters outside [A-Z0-9_]+"; } } ProcessEnumInitializers(\@arr,\@initializers, $enumtypename, \%SAI_DEFINES); # TODO stable sort values based on calculated values from initializer (https://perldoc.perl.org/sort) # TODO add param to disable this # remove unnecessary attributes my @values = @{ $SAI_ENUMS{$enumtypename}{values} }; push @ALL_ENUMS, @values; my @ranges = grep(/^SAI_\w+(RANGE_BASE)$/, @values); $SAI_ENUMS{$enumtypename}{ranges} = \@ranges; my @custom = grep(/^SAI_\w+_CUSTOM_RANGE_(START|END)$/, @values); $SAI_ENUMS_CUSTOM_RANGES{$enumtypename}{customranges} = \@custom; @values = grep(!/^SAI_\w+_(START|END)$/, @values); @values = grep(!/^SAI_\w+(RANGE_BASE)$/, @values); if ($enumtypename =~ /^(sai_\w+)_t$/) { my $valuescount = @values; # allow empty enum on extensions if ($valuescount == 0 and not $enumtypename =~ /_extensions_t$/) { LogError "enum $enumtypename is empty, after removing suffixed entries _START/_END/_RANGE_BASE"; LogError " those suffixes are reserved for range markers and are removed by metadata parser, don't use them"; LogError " as actual part of valid enum name, take a look at sai_udf_group_type_t for valid usage"; next; } if ($valuescount > 0) { my $last = $values[$#values]; if ($last eq "${enumprefix}MAX") { $last = pop @values; LogInfo "Removing last element $last"; } } } else { LogError "NON sai Enum $enumtypename"; } $SAI_ENUMS{$enumtypename}{values} = \@values; if (not $enumtypename =~ /^(sai_(\w+)_attr_(extensions_)?)t$/) { for my $ev (@{ $memberdef->{enumvalue} }) { my $enumvaluename = $ev->{name}[0]; my $eitemd = ExtractDescription($enumtypename, $enumvaluename, $ev->{detaileddescription}[0]); ProcessEnumItemDescription($enumtypename, $enumvaluename, $eitemd); } next; } # ENUM ATTRIBUTES PROCESSED BELOW # TODO put to SAI_ATTR_ENUMS my $prefix = uc$1; # remove unnecessary attributes @values = @{ $SAI_ENUMS{$enumtypename}{values} }; @values = grep(!/^${prefix}(CUSTOM_RANGE_|FIELD_|ACTION_)?(START|END)$/, @values); $SAI_ENUMS{$enumtypename}{values} = \@values; # this is attribute for my $ev (@{ $memberdef->{enumvalue} }) { my $enumvaluename = $ev->{name}[0]; my $desc = ExtractDescription($enumtypename, $enumvaluename, $ev->{detaileddescription}[0]); my $brief = ExtractDescription($enumtypename, $enumvaluename, $ev->{briefdescription}[0]); ProcessDescription($enumtypename, $enumvaluename, $desc, $brief); # remove ignored attributes from enums from list # since enum must match attribute list size if (defined $METADATA{$enumtypename}{$enumvaluename}{ignore}) { @values = grep(!/^$enumvaluename$/, @values); $SAI_ENUMS{$enumtypename}{values} = \@values; next; } if ($enumvaluename =~ /^(SAI_\w+_)MIN$/) { my $prefix = $1; my $range = $METADATA{$enumtypename}{$enumvaluename}{range}; if (not defined $range) { # XXX we can relax this and generate range only if range tag is defined LogWarning "attribute $enumvaluename must have \@range tag"; next; } my $rangeLimit = 10; if ($range > $rangeLimit) { # let's not generate too many attributes that will not be used LogInfo "Limiting range from $range to $rangeLimit"; $range = $rangeLimit; } # we assume zero is reserved for *_MIN and last value for *_MAX my @rangeElements = (); for (my $idx = 1; $idx < $range; ++$idx) { my $attrid = "${prefix}$idx"; push@rangeElements, $attrid; my $attr = ShallowCopyAttrEnum($METADATA{$enumtypename}{$enumvaluename}); $attr->{attrid} = $attrid; $METADATA{$enumtypename}{$attrid} = $attr; $EXTRA_RANGE_DEFINES{$attrid} = "($enumvaluename + $idx)"; } # update enum values (this could be done in previous step my @values = @{ $SAI_ENUMS{$enumtypename}{values} }; my ($index) = grep { $values[$_] =~ /^$enumvaluename$/ } 0..$#values; splice @values, $index+1,0, @rangeElements; $SAI_ENUMS{$enumtypename}{values} = \@values; } } } } sub ShallowCopyAttrEnum { my $refHash = shift; my %hash = %{ $refHash }; my %attr = map { $_, $hash{$_} } keys %hash; return \%attr; } sub ProcessPrimitiveTypedef { my ($typedeftype, $definition) = @_; if (not $definition =~ /^typedef (u?int\d+_t) ((sai_\w+_t)(\[\d+\])?)$/) { LogError("unrecognized primitive type: '$definition'"); return; } my $base = $1; my $fulltype = $2; my $name = $3; $PRIMITIVE_TYPES{$name}{base} = $base; $PRIMITIVE_TYPES{$name}{fulltype} = $fulltype; $PRIMITIVE_TYPES{$name}{isarray} = ( $fulltype =~ /\[\d+\]/ ) ? 1 : 0; } sub ProcessTypedefSection { my $section = shift; for my $memberdef (@{ $section->{memberdef} }) { next if not $memberdef->{kind} eq "typedef"; my $id = $memberdef->{id}; my $typedefname = $memberdef->{name}[0]; my $typedeftype; $typedeftype = $memberdef->{type}[0] if ref $memberdef->{type}[0] eq ""; $typedeftype = $memberdef->{type}[0]->{content} if ref $memberdef->{type}[0] eq "HASH"; ProcessPrimitiveTypedef($typedeftype, $memberdef->{definition}[0]) if $typedeftype =~ /^u?int\d+_t$/; if ($typedeftype =~ /^struct/) { # record structs for later serialization # this will also include structs from metadata $ALL_STRUCTS{$typedefname} = 1; next; } if ($typedefname =~ /^sai_\w+_fn$/) { $FUNCTION_DEF{$typedefname} = $memberdef->{definition}[0]; } if ($typedefname =~ /^sai_\w+_notification_fn$/) { if (not $typedeftype =~ /void\(\*/) { LogWarning "notification $typedefname should return void, but got '$typedeftype'"; next; } ProcessNotifications($memberdef, $typedefname); next; } # TODO add callback handling next if not $typedeftype =~ /^enum/; if (not defined $SAI_ENUMS{$typedefname}) { LogError "enum $typedefname has no typedef enum $typedefname"; next; } next if not $typedefname =~ /^sai_(\w+)_attr_(extensions_)?t$/; # this enum is attribute definition for object $SAI_ENUMS{$typedefname}{objecttype} = "SAI_OBJECT_TYPE_" . uc($1); } } sub ProcessNotificationCount { my ($ntfName, $tagValue, $previousTagValue) = @_; my %count = (); %count = %{ $previousTagValue } if defined $previousTagValue; if (not $tagValue =~ /^(\w+)\[(\w+|\d+)\]$/g) { LogError "unable to parse count '$tagValue' on $ntfName"; return undef; } my $pointerParam = $1; my $countParam = $2; $count{$pointerParam} = $countParam; LogDebug "adding count $pointerParam\[$countParam\] on $ntfName"; if ($pointerParam eq $countParam) { LogError "count '$pointerParam' can't point to itself in \@count on $ntfName"; undef; } return \%count; } sub ProcessNotificationObjects { # # object type for attribute params are described in # notification descripnon, it would be easier # if they would be described on params itself # my ($ntfName, $tagValue, $previousTagValue) = @_; my %objectTypes = (); %objectTypes = %{ $previousTagValue } if defined $previousTagValue; if (not $tagValue =~ /^(\w+)\s+(SAI_OBJECT_TYPE_\w+)$/g) { LogError "invalid object type tag value '$tagValue' in $ntfName"; return undef; } $objectTypes{$1} = $2; LogDebug "adding object type $2 on param $1 in $ntfName"; return \%objectTypes; } my %NOTIFICATION_TAGS = ( "count" , \&ProcessNotificationCount, "objects" , \&ProcessNotificationObjects, ); sub ProcessNotificationDescription { my ($refNtf, $desc) = @_; $refNtf->{desc} = $desc; my $ntfName = $refNtf->{name}; $desc =~ s/@@/\n@@/g; while ($desc =~ /@@(\w+)(.*)/g) { my $tag = $1; my $value = Trim($2); if (not defined $NOTIFICATION_TAGS{$tag}) { LogError "unrecognized tag '$tag' on $ntfName: $value"; next; } LogDebug "processing tag '$tag' on $ntfName"; $refNtf->{$tag} = $NOTIFICATION_TAGS{$tag}->($ntfName, $value, $refNtf->{$tag}); } } sub ProcessNotifications { my ($member, $typedefname) = @_; my $args = $member->{argsstring}[0]; $args =~ s/[()]//g; my @params = split/,/,$args; my %N = (name => $typedefname); my $desc = ExtractDescription($typedefname, $typedefname, $member->{detaileddescription}[0]); ProcessNotificationDescription(\%N, $desc); my @Members = (); my $idx = 0; my $ParamRegex = '^\s*_In_ ((const )?\w+\s*?\*?)\s*(\w+)$'; my @keys = (); for my $param (@params) { # NOTE multple pointers or consts are not supported if (not $param =~ /$ParamRegex/) { LogWarning "can't match param '$param' on $typedefname"; return } my %M = (); my $type = $1; my $name = $3; push @keys, $name; if (defined $N{objects} and defined $N{objects}{$name}) { my @objects = (); push @objects, $N{objects}{$name}; $M{objects} = \@objects; } $M{param} = $param; $M{type} = $type; $M{name} = $name; $M{idx} = $idx; $M{file} = $member->{location}[0]->{file}; push @Members, \%M; $N{membersHash}{ $name } = \%M; $idx++; } # second pass is needed if count param is defined after data pointer for my $param (@params) { next if (not $param =~ /$ParamRegex/); my $type = $1; my $name = $3; next if not $type =~ /\*/; if (not defined $N{count} and not defined $N{count}->{$name}) { LogWarning "type '$type' is pointer, \@count is required, but missing on $typedefname"; next; } my $countParamName = $N{count}->{$name}; $N{membersHash}->{$name}{count} = $countParamName; if ($countParamName =~ /^(\d+)$/) { # count is given explicit next; } if (not defined $N{membersHash}->{$countParamName}) { LogWarning "count param name '$countParamName' on $typedefname is not defined"; next; } my $countType = $N{membersHash}->{$countParamName}{type}; if (not $countType =~ /^(uint32_t|sai_size_t)$/) { LogWarning "param '$countParamName' used as count for param '$name' ($typedefname)"; LogWarning " is wrong type '$countType' allowed: (uint32_t|sai_size_t)"; } } $N{params} = \@params; $N{keys} = \@keys; $N{members} = \@Members; $N{ismethod} = 1; $N{baseName} = $1 if $typedefname =~ /^sai_(\w+_notification)_fn$/; $NOTIFICATIONS{$typedefname} = \%N; } sub ProcessFunctionSection { my $section = shift; for my $memberdef (@{ $section->{memberdef} }) { next if not $memberdef->{kind} eq "function"; #print Dumper($memberdef); my $name = $memberdef->{name}[0]; my $file = $memberdef->{location}[0]->{file}; next if not $file =~ m!inc/sai\w*.h!; LogError "api $name not starting with sai_! " if not $name =~ /^sai_\w+$/; #print Dumper($memberdef); my $def = $memberdef->{definition}[0]; my $type = $memberdef->{type}[0]; $type = $1 if $def =~ /^(\w+) sai_\w+$/; $GLOBAL_APIS{$name}{name} = $name; $GLOBAL_APIS{$name}{args} = $memberdef->{argsstring}[0]; $GLOBAL_APIS{$name}{type} = $type; } } sub ProcessXmlFile { my $file = shift; my $ref = ReadXml $file; my @sections = @{ $ref->{compounddef}[0]->{sectiondef} }; for my $section (@sections) { ProcessDefineSection($section) if ($section->{kind} eq "define"); ProcessEnumSection($section) if ($section->{kind} eq "enum"); ProcessTypedefSection($section) if $section->{kind} eq "typedef"; ProcessFunctionSection($section) if $section->{kind} eq "func"; } } sub ProcessFlagsType { my ($typedef, $flagstype) = @_; return "SAI_ENUM_FLAGS_TYPE_NONE" if not defined $flagstype; return "SAI_ENUM_FLAGS_TYPE_NONE" if $flagstype eq "none"; return "SAI_ENUM_FLAGS_TYPE_STRICT" if $flagstype eq "strict"; return "SAI_ENUM_FLAGS_TYPE_MIXED" if $flagstype eq "mixed"; return "SAI_ENUM_FLAGS_TYPE_RANGES" if $flagstype eq "ranges"; return "SAI_ENUM_FLAGS_TYPE_FREE" if $flagstype eq "free"; LogError "wrong flags type '$flagstype' on $typedef, expected $ENUM_FLAGS_TYPES"; return "WRONG"; } sub ProcessSingleEnum { my ($key, $typedef, $prefix) = @_; $prefix =~ s/EXTENSIONS_$// if ($typedef =~ /_extensions_t$/); my $enum = $SAI_ENUMS{$key}; my @values = @{$enum->{values}}; my $flags = (defined $enum->{flagsenum}) ? $enum->{flagsenum} : "false"; my $flagstype = ProcessFlagsType($typedef, $enum->{flagstype}); WriteSource "const $typedef sai_metadata_${typedef}_enum_values[] = {"; for my $value (@values) { LogWarning "Value $value of $typedef must use only capital letters" if $value =~ /[a-z]/; LogWarning "Value $value of $typedef is not prefixed as $prefix" if not $value =~ /^$prefix/; WriteSource "$value,"; } WriteSource "-1"; # guard WriteSource "};"; WriteSource "const char* const sai_metadata_${typedef}_enum_values_names[] = {"; for my $value (@values) { WriteSource "\"$value\","; } WriteSource "NULL"; WriteSource "};"; WriteSource "const char* const sai_metadata_${typedef}_enum_values_short_names[] = {"; for my $value (@values) { $value =~ s/^${prefix}//; WriteSource "\"$value\","; } WriteSource "NULL"; WriteSource "};"; if (defined $enum->{ignoreval}) { my @ignoreval = @{ $enum->{ignoreval} }; WriteSource "const $typedef sai_metadata_${typedef}_enum_ignore_values[] = {"; for my $value (@ignoreval) { WriteSource "$value,"; } WriteSource "-1"; # guard WriteSource "};"; WriteSource "const char* const sai_metadata_${typedef}_enum_ignore_values_names[] = {"; for my $value (@ignoreval) { WriteSource "\"$value\","; } WriteSource "NULL"; WriteSource "};"; } my $count = @values; WriteHeader "extern const sai_enum_metadata_t sai_metadata_enum_$typedef;"; WriteSource "const sai_enum_metadata_t sai_metadata_enum_$typedef = {"; WriteSource ".name = \"${typedef}\","; WriteSource ".valuescount = $count,"; WriteSource ".values = (const int*)sai_metadata_${typedef}_enum_values,"; WriteSource ".valuesnames = sai_metadata_${typedef}_enum_values_names,"; WriteSource ".valuesshortnames = sai_metadata_${typedef}_enum_values_short_names,"; WriteSource ".containsflags = $flags,"; WriteSource ".flagstype = $flagstype,"; if (defined $enum->{ignoreval}) { WriteSource ".ignorevalues = (const int*)sai_metadata_${typedef}_enum_ignore_values,"; WriteSource ".ignorevaluesnames = sai_metadata_${typedef}_enum_ignore_values_names,"; } else { WriteSource ".ignorevalues = NULL,"; WriteSource ".ignorevaluesnames = NULL,"; } my $ot = ($typedef =~ /^sai_(\w+)_attr_t/) ? uc("SAI_OBJECT_TYPE_$1") : "SAI_OBJECT_TYPE_NULL"; #my $ot = ($typedef =~ /^sai_(\w+)_attr_(extensions_)?t/) ? uc("SAI_OBJECT_TYPE_$1") : "SAI_OBJECT_TYPE_NULL"; WriteSource ".objecttype = (sai_object_type_t)$ot,"; WriteSource "};"; return $count; } sub ProcessExtraRangeDefines { WriteSectionComment "Extra range defines"; for my $key (sort keys %EXTRA_RANGE_DEFINES) { WriteHeader "#define $key $EXTRA_RANGE_DEFINES{$key}"; } } sub CreateSourceIncludes { WriteSourceSectionComment "Includes"; WriteSource "#include <stdio.h>"; WriteSource "#include <string.h>"; WriteSource "#include <stdlib.h>"; WriteSource "#include <stddef.h>"; WriteSource "#include \"saimetadata.h\""; } sub CreateSourcePragmaPush { WriteSourceSectionComment "Pragma diagnostic push"; # # because we are merging extension attributes into existing # enums, new versions of gcc can warn when 2 different enums # are mixed, so lets ignore this warning using pragmas # WriteSource "#pragma GCC diagnostic push"; WriteSource "#pragma GCC diagnostic ignored \"-Wpragmas\""; WriteSource "#pragma GCC diagnostic ignored \"-Wenum-conversion\""; } sub CreateDeclareEveryEntryMacro { WriteSectionComment "Every entry macros"; WriteHeader "#define SAI_METADATA_DECLARE_EVERY_ENTRY(SAI_USER_X_ENTRY_MACRO) \\"; my @rawnames = GetNonObjectIdStructNames(); for my $name (sort @rawnames) { my $uc = uc($name); WriteHeader " SAI_USER_X_ENTRY_MACRO($uc,$name) \\"; } WriteHeader ""; WriteHeader "#define SAI_METADATA_DECLARE_EVERY_BULK_ENTRY(SAI_USER_X_BULK_ENTRY_MACRO) \\"; @rawnames = GetNonObjectIdStructNamesWithBulkApi(); for my $name (sort @rawnames) { my $uc = uc($name); WriteHeader " SAI_USER_X_BULK_ENTRY_MACRO($uc,$name) \\"; } WriteHeader ""; } sub CreateMetadataHeaderAndSource { WriteSectionComment "Enums metadata"; for my $key (sort keys %SAI_ENUMS) { if (not $key =~ /^((sai_\w+_)t)$/) { LogWarning "Enum typedef $key is not matching SAI format"; next; } ProcessSingleEnum($key, $1, uc $2); } # all enums WriteHeader "extern const sai_enum_metadata_t* const sai_metadata_all_enums[];"; WriteSource "const sai_enum_metadata_t* const sai_metadata_all_enums[] = {"; for my $key (sort keys %SAI_ENUMS) { if (not $key =~ /^((sai_\w+_)t)$/) { LogWarning "Enum typedef $key is not matching SAI format"; next; } my $typedef = $1; WriteSource "&sai_metadata_enum_$typedef,"; } WriteSource "NULL"; WriteSource "};"; my $count = keys %SAI_ENUMS; WriteHeader "extern const size_t sai_metadata_all_enums_count;"; WriteSource "const size_t sai_metadata_all_enums_count = $count;"; WriteHeader "extern const sai_enum_metadata_t* const sai_metadata_attr_enums[];"; WriteSource "const sai_enum_metadata_t* const sai_metadata_attr_enums[] = {"; $count = 0; for my $key (sort keys %SAI_ENUMS) { next if not $key =~ /^(sai_\w+_attr_t)$/; my $typedef = $1; WriteSource "&sai_metadata_enum_$typedef,"; $count++; } WriteSource "NULL"; WriteSource "};"; WriteHeader "extern const size_t sai_metadata_attr_enums_count;"; WriteSource "const size_t sai_metadata_attr_enums_count = $count;"; # attr enums as object types for sanity check WriteSource "const sai_object_type_t sai_metadata_object_types[] = {"; for my $key (sort keys %SAI_ENUMS) { if (not $key =~ /^(sai_(\w+)_attr_t)$/) { next; } my $typedef = $1; my $objtype = $2; WriteSource "SAI_OBJECT_TYPE_" . uc($objtype). ","; } WriteSource "-1"; WriteSource "};"; } sub ProcessType { my ($attr, $type) = @_; if (not defined $type) { LogError "type is not defined for $attr"; return ""; } if ($type =~ /^sai_acl_field_data_t (bool|sai_\w+_t)$/) { my $prefix = "SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA"; return "${prefix}_BOOL" if $1 eq "bool"; return "${prefix}_$ACL_FIELD_TYPES_TO_VT{$1}" if defined $ACL_FIELD_TYPES_TO_VT{$1}; if (not defined $SAI_ENUMS{$1}) { LogError "invalid enum specified acl field '$type' on $attr"; return ""; } return "${prefix}_INT32"; } if ($type =~ /^sai_acl_field_data_mask_t (bool|sai_\w+_t)$/) { # TODO this is temporary solution, since mask should have it's own attribute type # and not reuseing existing ones from aclfield, this provides confusion my $prefix = "SAI_ATTR_VALUE_TYPE_ACL_FIELD_DATA"; return "${prefix}_BOOL" if $1 eq "bool"; return "${prefix}_$ACL_FIELD_TYPES_TO_VT{$1}" if defined $ACL_FIELD_TYPES_TO_VT{$1}; if (not defined $SAI_ENUMS{$1}) { LogError "invalid enum specified acl field '$type' on $attr"; return ""; } return "${prefix}_INT32"; } if ($type =~ /^sai_acl_action_data_t (bool|sai_\w+_t)$/) { my $prefix = "SAI_ATTR_VALUE_TYPE_ACL_ACTION_DATA"; return "${prefix}_BOOL" if $1 eq "bool"; return "${prefix}_$ACL_ACTION_TYPES_TO_VT{$1}" if defined $ACL_ACTION_TYPES_TO_VT{$1}; if (not defined $SAI_ENUMS{$1}) { LogError "invalid enum specified acl action '$type' on $attr"; return ""; } return "${prefix}_INT32"; } if ($type =~ /^sai_s32_list_t (sai_\w+_t)$/) { if (not defined $SAI_ENUMS{$1}) { LogError "invalid enum list specified '$type' on $attr"; return ""; } return "SAI_ATTR_VALUE_TYPE_INT32_LIST"; } if ($type =~ /^(sai_\w+_t)$/) { my $prefix = "SAI_ATTR_VALUE_TYPE"; return "${prefix}_$VALUE_TYPES_TO_VT{$1}" if defined $VALUE_TYPES_TO_VT{$1}; if (not defined $SAI_ENUMS{$1}) { LogError "invalid enum specified '$type' on $attr"; return ""; } return "${prefix}_INT32"; } if ($type eq "bool") { return "SAI_ATTR_VALUE_TYPE_BOOL"; } if ($type eq "char") { return "SAI_ATTR_VALUE_TYPE_CHARDATA"; } if ($type =~ /^sai_pointer_t sai_\w+_fn$/) { return "SAI_ATTR_VALUE_TYPE_POINTER"; } LogError "invalid type '$type' for $attr"; return ""; } sub ProcessFlags { my ($value,$flags) = @_; if (not defined $flags) { LogError "flags are not defined for $value"; return ""; } my @flags = @{ $flags }; @flags = map {s/^/SAI_ATTR_FLAGS_/; $_; } @flags; return "(sai_attr_flags_t)(" . join("|",@flags) . ")"; } sub ProcessAllowNull { my ($value,$allownull) = @_; return $allownull if defined $allownull; return "false"; } sub ProcessIsResourceType { my ($value,$isresourcetype) = @_; return $isresourcetype if defined $isresourcetype; return "false"; } sub ProcessIsDeprecatedType { my ($value, $deprecated) = @_; return $deprecated if defined $deprecated; return "false"; } sub ProcessRelaxedType { my ($value, $relaxed) = @_; return $relaxed if defined $relaxed; return "false"; } sub ProcessObjects { my ($attr, $objects) = @_; return "NULL" if not defined $objects; WriteSource "const sai_object_type_t sai_metadata_${attr}_allowed_objects[] = {"; my @all = @{ $SAI_ENUMS{sai_object_type_t}{values} }; for my $obj (@{ $objects }) { if (not defined $OBJECT_TYPE_MAP{$obj}) { LogError "unknown object type '$obj' on $attr"; return ""; } WriteSource "$obj,"; } WriteSource "-1"; # guard WriteSource "};"; return "sai_metadata_${attr}_allowed_objects"; } sub ProcessObjectsLen { my ($value, $objects) = @_; return "0" if not defined $objects; my $count = @{ $objects }; return $count; } sub ProcessDefaultValueType { my ($attr, $default) = @_; return "SAI_DEFAULT_VALUE_TYPE_NONE" if not defined $default; return "SAI_DEFAULT_VALUE_TYPE_CONST" if $default =~ /^SAI_NULL_OBJECT_ID$/; return "SAI_DEFAULT_VALUE_TYPE_SWITCH_INTERNAL" if $default =~ /^internal$/; return "SAI_DEFAULT_VALUE_TYPE_CONST" if $default =~ /^(true|false|const|NULL|$NUMBER_REGEX|SAI_\w+)$/ and not $default =~ /_ATTR_|SAI_OBJECT_TYPE_/; return "SAI_DEFAULT_VALUE_TYPE_EMPTY_LIST" if $default =~ /^empty$/; return "SAI_DEFAULT_VALUE_TYPE_VENDOR_SPECIFIC" if $default =~ /^vendor$/; return "SAI_DEFAULT_VALUE_TYPE_ATTR_VALUE" if $default =~ /^attrvalue SAI_\w+$/ and $default =~ /_ATTR_/; return "SAI_DEFAULT_VALUE_TYPE_ATTR_RANGE" if $default =~ /^attrrange SAI_\w+$/ and $default =~ /_ATTR_/; return "SAI_DEFAULT_VALUE_TYPE_CONST" if $default =~ /^0\.0\.0\.0$/; return "SAI_DEFAULT_VALUE_TYPE_CONST" if $default =~ /^00:00:00:00:00:00$/; return "SAI_DEFAULT_VALUE_TYPE_CONST" if $default eq "disabled"; return "SAI_DEFAULT_VALUE_TYPE_CONST" if $default eq "\"\""; return "SAI_DEFAULT_VALUE_TYPE_CONST" if $default =~ /^ffff\:ffff\:ffff\:ffff\:ffff\:ffff\:ffff\:ffff$/; LogError "invalid default value type '$default' on $attr"; return ""; } sub ProcessDefaultValue { my ($attr, $default, $type) = @_; return "NULL" if not defined $default; my $val = "const sai_attribute_value_t sai_metadata_${attr}_default_value"; if ($default =~ /^(true|false)$/ and $type eq "bool") { WriteSource "$val = { .booldata = $default };"; } elsif ($default =~ /^SAI_NULL_OBJECT_ID$/ and $type =~ /^sai_object_id_t$/) { WriteSource "$val = { .oid = $default };"; } elsif ($default =~ /^SAI_\w+$/ and $type =~ /^sai_\w+_t$/ and not defined $VALUE_TYPES{$type}) { WriteSource "$val = { .s32 = $default };"; } elsif ($default =~ /^0$/ and $type =~ /^sai_acl_field_data_t (sai_u?int\d+_t)/) { WriteSource "$val = { 0 };"; } elsif ($default =~ /^ffff\:ffff\:ffff\:ffff\:ffff\:ffff\:ffff\:ffff$/ and $type =~ /^sai_acl_field_data_mask_t (sai_ip6_t)/) { WriteSource "$val = { .ip6 = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} };"; } elsif ($default =~ /^0$/ and $type =~ /^sai_acl_action_data_t (sai_u?int\d+_t)/) { WriteSource "$val = { 0 };"; } elsif ($default =~ /^$NUMBER_REGEX$/ and $type =~ /^sai_u?int\d+_t/) { WriteSource "$val = { .$VALUE_TYPES{$type} = $default };"; } elsif ($default =~ /^NULL$/ and $type =~ /^(sai_pointer_t) (sai_\w+_fn)$/) { WriteSource "$val = { .$VALUE_TYPES{$1} = $default };"; } elsif ($default =~ /^(attrvalue|attrrange|vendor|empty|const|internal)/) { return "NULL"; } elsif ($default =~ /^NULL$/ and $type =~ /^sai_pointer_t/) { LogError "missing typedef function in format 'sai_\\w+_fn' on $attr ($type)"; } elsif ($default =~ /^0\.0\.0\.0$/ and $type =~ /^(sai_ip_address_t)/) { # ipv4 address needs to be converted to uint32 number so we support now only 0.0.0.0 WriteSource "$val = { .$VALUE_TYPES{$1} = { .addr_family = SAI_IP_ADDR_FAMILY_IPV4, .addr = { .ip4 = 0 } } };"; } elsif ($default =~ /^disabled$/ and $type =~ /^(sai_acl_action_data_t|sai_acl_field_data_t) /) { WriteSource "$val = { .$VALUE_TYPES{$1} = { .enable = false } };"; } elsif ($default =~ /^""$/ and $type eq "char") { WriteSource "$val = { .chardata = { 0 } };"; } elsif ($default =~ /^0\.0\.0\.0$/ and $type =~ /^(sai_ip4_t)/) { WriteSource "$val = { 0 };"; } elsif ($default =~ /^00:00:00:00:00:00$/ and $type =~ /^(sai_mac_t)/) { WriteSource "$val = { .mac = { 0, 0, 0, 0, 0, 0 } };"; } else { LogError "invalid default value '$default' on $attr ($type)"; } return "&sai_metadata_${attr}_default_value"; } sub ProcessDefaultValueObjectType { my ($attr, $value, $type) = @_; $value = "" if not defined $value; return "SAI_OBJECT_TYPE_$2" if $value =~ /^(attrvalue|attrrange) SAI_(\w+)_ATTR_\w+$/; return "SAI_OBJECT_TYPE_NULL"; } sub ProcessDefaultValueAttrId { my ($attr, $value, $type) = @_; $value = "" if not defined $value; return $2 if $value =~ /^(attrvalue|attrrange) ((SAI_\w+)_ATTR_\w+)$/; return "SAI_INVALID_ATTRIBUTE_ID"; } sub ProcessStoreDefaultValue { my ($attr, $value, $flags) = @_; # at this point we can't determine whether object is non object id if (defined $value and $value =~ /vendor|attrvalue/) { return "true"; } my @flags = @{ $flags }; $flags = "@flags"; if ($flags =~ /MANDATORY/ and $flags =~ /CREATE_AND_SET/) { return "true"; } return "false"; } sub ProcessIsEnum { my ($value, $type) = @_; return "false" if not defined $type; return "true" if $type =~ /^sai_\w+_t$/ and not defined $VALUE_TYPES{$type}; return "true" if $type =~ /^sai_acl_field_data_t (sai_\w+_t)$/ and not defined $ACL_FIELD_TYPES{$1}; return "true" if $type =~ /^sai_acl_field_data_mask_t (sai_\w+_t)$/ and not defined $ACL_FIELD_TYPES{$1}; return "true" if $type =~ /^sai_acl_action_data_t (sai_\w+_t)$/ and not defined $ACL_ACTION_TYPES{$1}; return "false"; } sub ProcessIsEnumList { my ($attr, $type) = @_; return "false" if not defined $type; return "true" if $type =~ /^sai_s32_list_t sai_\w+_t$/; return "false"; } sub ProcessEnumMetadata { my ($attr, $type) = @_; return "NULL" if not defined $type; return "&sai_metadata_enum_$1" if $type =~ /^(sai_\w+_t)$/ and not defined $VALUE_TYPES{$type}; return "&sai_metadata_enum_$1" if $type =~ /^sai_acl_field_data_t (sai_\w+_t)$/ and not defined $ACL_FIELD_TYPES{$1}; return "&sai_metadata_enum_$1" if $type =~ /^sai_acl_field_data_mask_t (sai_\w+_t)$/ and not defined $ACL_FIELD_TYPES{$1}; return "&sai_metadata_enum_$1" if $type =~ /^sai_acl_action_data_t (sai_\w+_t)$/ and not defined $ACL_ACTION_TYPES{$1}; return "&sai_metadata_enum_$1" if $type =~ /^sai_s32_list_t (sai_\w+_t)$/; return "NULL"; } sub ProcessIsVlan { my ($attr, $value, $type) = @_; if (not defined $value and $type =~ /uint16/) { LogWarning "$attr is $type, must define TAG isvlan"; } return "false" if not defined $value; return $value; } sub ProcessGetSave { my ($attr, $value) = @_; return "false" if not defined $value; return $value; } sub ProcessConditionType { my ($attr, $value) = @_; return "SAI_ATTR_CONDITION_TYPE_NONE" if not defined $value; return @{$value}[0]; } sub ProcessConditionsGeneric { my ($attr, $conditions, $enumtype, $name) = @_; return "NULL" if not defined $conditions; my @conditions = @{ $conditions }; my $ctype = shift @conditions; if (not $ctype =~ /^SAI_ATTR_CONDITION_TYPE_(AND|OR|MIXED)$/) { LogError "unsupported condition type $ctype on $attr"; return ""; } my $count = 0; my @values = (); for my $cond (@conditions) { if ($ctype eq "SAI_ATTR_CONDITION_TYPE_MIXED") { if ($cond =~ /^SAI_ATTR_CONDITION_TYPE_(AND|OR)$/) { WriteSource "const sai_attr_condition_t sai_metadata_${name}_${attr}_$count = {"; WriteSource ".attrid = SAI_INVALID_ATTRIBUTE_ID,"; WriteSource ".condition = { 0 },"; WriteSource ".op = SAI_CONDITION_OPERATOR_EQ,"; WriteSource ".type = $cond"; WriteSource "};"; $count++; next; } } if (not $cond =~ /^(SAI_\w+) == (true|false|SAI_\w+|$NUMBER_REGEX)$/) { LogError "invalid condition '$cond' on $attr"; return ""; } my $attrid = $1; my $val = $2; my $main_attr = $1 if $attr =~ /^SAI_(\w+?)_ATTR_/; my $cond_attr = $1 if $attrid =~ /^SAI_(\w+?)_ATTR_/; if ($main_attr ne $cond_attr) { LogError "$name attribute $attr has condition from different object $attrid"; return ""; } WriteSource "const sai_attr_condition_t sai_metadata_${name}_${attr}_$count = {"; my $attrType = lc("$1t") if $attrid =~ /^(SAI_\w+_ATTR_)/; my $enumTypeName = $METADATA{$attrType}{$attrid}{type}; if (not defined $enumTypeName) { LogError("failed to find attribute ${attrType}::${attrid} when processing $attrid"); next; } if ($val eq "true" or $val eq "false") { WriteSource ".attrid = $attrid,"; WriteSource ".condition = { .booldata = $val },"; WriteSource ".op = SAI_CONDITION_OPERATOR_EQ,"; WriteSource ".type = SAI_ATTR_CONDITION_TYPE_NONE"; } elsif ($val =~ /^SAI_/) { WriteSource ".attrid = $attrid,"; WriteSource ".condition = { .s32 = $val },"; WriteSource ".op = SAI_CONDITION_OPERATOR_EQ,"; WriteSource ".type = SAI_ATTR_CONDITION_TYPE_NONE"; my $attrType = lc("$1t") if $attrid =~ /^(SAI_\w+_ATTR_)/; my $enumTypeName = $METADATA{$attrType}{$attrid}{type}; if (not defined $enumTypeName) { LogError("failed to find attribute ${attrType}::${attrid} when processing $attrid"); next; } if (defined $SAI_ENUMS{$enumTypeName}) { # this condition is enum condition, check if condition value # belongs to that enum my @values = @{ $SAI_ENUMS{$enumTypeName}{values} }; if (not grep( /^$val$/, @values)) { LogError "condition value '$val' in '$cond' on $attr is not present in $enumTypeName"; } } } elsif ($val =~ /^$NUMBER_REGEX$/ and $enumTypeName =~ /^sai_u?int(\d+)_t$/) { my $n = $1; my $item = ($enumTypeName =~ /uint/) ? "u$n" : "s$n"; WriteSource ".attrid = $attrid,"; WriteSource ".condition = { .$item = $val },"; WriteSource ".op = SAI_CONDITION_OPERATOR_EQ,"; WriteSource ".type = SAI_ATTR_CONDITION_TYPE_NONE"; } else { LogError "unknown $name value: $val on $attr $enumtype"; return ""; } WriteSource "};"; $count++; } WriteSource "const sai_attr_condition_t* const sai_metadata_${name}s_${attr}\[\] = {"; $count = 0; for my $cond (@conditions) { WriteSource "&sai_metadata_${name}_${attr}_$count,"; $count++; } WriteSource "NULL"; WriteSource "};"; return "sai_metadata_${name}s_${attr}"; } sub ProcessConditions { ProcessConditionsGeneric(@_, "condition"); } sub ProcessConditionsLen { my ($attr, $value) = @_; return "0" if not defined $value; my @conditions = @{ $value }; # NOTE: number returned is -1 since first item is condition type return $#conditions; } sub ProcessValidOnlyType { my ($attr, $value) = @_; return "SAI_ATTR_CONDITION_TYPE_NONE" if not defined $value; return @{$value}[0]; } sub ProcessValidOnly { ProcessConditionsGeneric(@_, "validonly"); } sub ProcessValidOnlyLen { my ($attr, $value) = @_; return "0" if not defined $value; my @conditions = @{ $value }; # NOTE: number returned is -1 since first item is condition type return $#conditions; } sub ProcessAllowRepeat { my ($attr, $value) = @_; return "false" if not defined $value; return $value; } sub ProcessAllowMixed { my ($attr, $value) = @_; return "false" if not defined $value; return $value; } sub ProcessAllowEmpty { my ($attr, $value, $default) = @_; return "true" if defined $default and $default eq "empty"; return "false" if not defined $value; return $value; } sub ProcessAttrName { my ($attr, $type) = @_; return "\"$attr\""; } sub ProcessIsCallback { my ($attr, $type) = @_; return "true" if defined $ATTR_TO_CALLBACK{$attr}; return "false"; } sub ProcessNotificationType { my ($attr, $type) = @_; return "SAI_SWITCH_NOTIFICATION_TYPE_$1" if $attr =~ /^SAI_SWITCH_ATTR_(\w+)_NOTIFY$/; return "-1"; } sub ProcessPointerType { my ($attr, $type) = @_; return "SAI_SWITCH_POINTER_TYPE_$1" if $attr =~ /^SAI_SWITCH_ATTR_(\w+)_NOTIFY$/; if (defined $ATTR_TO_CALLBACK{$attr}) { if ($ATTR_TO_CALLBACK{$attr} =~ /^sai_(\w+)_fn$/) { return "SAI_SWITCH_POINTER_TYPE_" . uc($1); } } return "-1"; } sub ProcessIsAclField { my $attr = shift; return "true" if $attr =~ /^SAI_ACL_ENTRY_ATTR_(USER_DEFINED_)?FIELD_\w+$/; return "true" if $attr =~ /^SAI_UDF_MATCH_ATTR_\w+_TYPE$/; return "false"; } sub ProcessIsAclAction { my $attr = shift; return "true" if $attr =~ /^SAI_ACL_ENTRY_ATTR_ACTION_\w+$/; return "false"; } sub ProcessIsAclMask { my ($attr, $type)= @_; return "true" if $type =~ /^sai_acl_field_data_mask_t /; return "false"; } sub ProcessBrief { my ($attr, $brief) = @_; if (not defined $brief or $brief eq "") { LogWarning "missing brief description for $attr"; return ""; } if ($brief =~ m!([^\x20-\x7e]|\\|")!is) { LogWarning "Not allowed char '$1' in brief description on $attr: $brief"; } if (length $brief > 200) { LogWarning "Long brief > 200 on $attr:\n - $brief"; } return "\"$brief\""; } sub ProcessIsPrimitive { my ($attr, $type) = @_; return "false" if $type =~ /(_list_t|acl_capability_t|_json_t)/; return "true"; } sub ProcessCapability { my ($attr, $type, $enummetadata) = @_; return "NULL" if not defined $CAPABILITIES{$attr}; my %CAP = %{ $CAPABILITIES{$attr} }; my $count = 0; my @values = (); for my $vid (sort keys %CAP) { my $enumcount = 0; my $enumvalues = "NULL"; if (defined $CAP{$vid}{enumcapability}) { if (not $enummetadata =~ /sai_metadata_enum_((sai_\w+_)t)/) { LogError "enum capability defined on $attr, but attribute is not enum"; next; } my $enumtype = $1; my $prefix = uc($2); my @values = @{ $CAP{$vid}{enumcapability} }; $enumcount = scalar @values; WriteSource "const int sai_metadata_enumcapability_${attr}_$vid\[\] = {"; my %vals = (); for my $v (@values) { LogError "enumvalue $v on capability $attr($vid) is not of type $enumtype" if not $v =~ /^$prefix/; LogError "enumvalue $v on capability $attr($vid) is already defined" if defined $vals{$v}; $vals{$v} = 1; WriteSource " $v,"; } WriteSource " -1,"; WriteSource "};"; $enumvalues = "sai_metadata_enumcapability_${attr}_$vid"; } if (not defined $CAP{$vid}{capability}) { LogError "capability for $attr is not defined"; next; } WriteSource "const sai_attr_capability_metadata_t sai_metadata_attr_capability_${attr}_$count = {"; my %cap = (); for my $c (@{ $CAP{$vid}{capability} }) { $cap{$c} = 1; } my $create = (defined $cap{"CREATE"}) ? "true" : "false"; my $get = (defined $cap{"GET"}) ? "true" : "false"; my $set = (defined $cap{"SET"}) ? "true" : "false"; WriteSource " .vendorid = $vid,"; WriteSource " .operationcapability = {"; WriteSource " .create_implemented = $create,"; WriteSource " .set_implemented = $set,"; WriteSource " .get_implemented = $get,"; WriteSource " },"; WriteSource " .enumvaluescount = $enumcount,"; WriteSource " .enumvalues = $enumvalues,"; WriteSource "};"; $count++; } WriteSource "const sai_attr_capability_metadata_t* const sai_metadata_attr_capability_${attr}\[\] = {"; $count = 0; for my $vid (sort keys %CAP) { WriteSource " &sai_metadata_attr_capability_${attr}_$count,"; $count++; } WriteSource " NULL"; WriteSource "};"; return "sai_metadata_attr_capability_$attr"; } sub ProcessCapabilityLen { my ($attr, $type) = @_; return 0 if not defined $CAPABILITIES{$attr}; return scalar(keys %{$CAPABILITIES{$attr}}); } sub ProcessIsExtensionAttr { my ($attr, $type) = @_; return "true" if defined $EXTENSIONS_ATTRS{$attr}; return "false"; } sub ProcessSingleObjectType { my ($typedef, $objecttype) = @_; my $enum = $SAI_ENUMS{$typedef}; my @values = @{ $enum->{values} }; for my $attr (@values) { if (not defined $METADATA{$typedef} or not defined $METADATA{$typedef}{$attr}) { LogError "metadata is missing for $attr"; next; } my %meta = %{ $METADATA{$typedef}{$attr} }; next if defined $meta{ignore}; $meta{type} = "" if not defined $meta{type}; my $type = ProcessType($attr, $meta{type}); my $attrname = ProcessAttrName($attr, $meta{type}); my $flags = ProcessFlags($attr, $meta{flags}); my $allownull = ProcessAllowNull($attr, $meta{allownull}); my $objects = ProcessObjects($attr, $meta{objects}); my $objectslen = ProcessObjectsLen($attr, $meta{objects}); my $allowrepeat = ProcessAllowRepeat($attr, $meta{allowrepeat}); my $allowmixed = ProcessAllowMixed($attr, $meta{allowmixed}); my $allowempty = ProcessAllowEmpty($attr, $meta{allowempty}, $meta{default}); my $defvaltype = ProcessDefaultValueType($attr, $meta{default}); my $defval = ProcessDefaultValue($attr, $meta{default}, $meta{type}); my $defvalot = ProcessDefaultValueObjectType($attr, $meta{default}, $meta{type}); my $defvalattrid = ProcessDefaultValueAttrId($attr, $meta{default}, $meta{type}); my $storedefaultval = ProcessStoreDefaultValue($attr, $meta{default}, $meta{flags}); my $isenum = ProcessIsEnum($attr, $meta{type}); my $isenumlist = ProcessIsEnumList($attr, $meta{type}); my $enummetadata = ProcessEnumMetadata($attr, $meta{type}); my $conditiontype = ProcessConditionType($attr, $meta{condition}); my $conditions = ProcessConditions($attr, $meta{condition}, $meta{type}); my $conditionslen = ProcessConditionsLen($attr, $meta{condition}); my $validonlytype = ProcessValidOnlyType($attr, $meta{validonly}); my $validonly = ProcessValidOnly($attr, $meta{validonly}, $meta{type}); my $validonlylen = ProcessValidOnlyLen($attr, $meta{validonly}); my $isvlan = ProcessIsVlan($attr, $meta{isvlan}, $meta{type}); my $getsave = ProcessGetSave($attr, $meta{getsave}); my $isaclfield = ProcessIsAclField($attr); my $isaclaction = ProcessIsAclAction($attr); my $isaclmask = ProcessIsAclMask($attr, $meta{type}); my $brief = ProcessBrief($attr, $meta{brief}); my $isprimitive = ProcessIsPrimitive($attr, $meta{type}); my $ntftype = ProcessNotificationType($attr, $meta{type}); my $iscallback = ProcessIsCallback($attr, $meta{type}); my $ptrtype = ProcessPointerType($attr, $meta{type}); my $cap = ProcessCapability($attr, $meta{type}, $enummetadata); my $caplen = ProcessCapabilityLen($attr, $meta{type}); my $isextensionattr = ProcessIsExtensionAttr($attr, $meta{type}); my $isresourcetype = ProcessIsResourceType($attr, $meta{isresourcetype}); my $isdeprecated = ProcessIsDeprecatedType($attr, $meta{deprecated}); my $isrelaxed = ProcessRelaxedType($attr, $meta{relaxed}); my $ismandatoryoncreate = ($flags =~ /MANDATORY/) ? "true" : "false"; my $iscreateonly = ($flags =~ /CREATE_ONLY/) ? "true" : "false"; my $iscreateandset = ($flags =~ /CREATE_AND_SET/) ? "true" : "false"; my $isreadonly = ($flags =~ /READ_ONLY/) ? "true" : "false"; my $iskey = ($flags =~ /KEY/) ? "true" : "false"; WriteSource "const sai_attr_metadata_t sai_metadata_attr_$attr = {"; WriteSource ".objecttype = (sai_object_type_t)$objecttype,"; WriteSource ".attrid = $attr,"; WriteSource ".attridname = $attrname,"; WriteSource ".brief = $brief,"; WriteSource ".attrvaluetype = $type,"; WriteSource ".flags = $flags,"; WriteSource ".allowedobjecttypes = $objects,"; WriteSource ".allowedobjecttypeslength = $objectslen,"; WriteSource ".allowrepetitiononlist = $allowrepeat,"; WriteSource ".allowmixedobjecttypes = $allowmixed,"; WriteSource ".allowemptylist = $allowempty,"; WriteSource ".allownullobjectid = $allownull,"; WriteSource ".isoidattribute = ($objectslen > 0),"; WriteSource ".defaultvaluetype = $defvaltype,"; WriteSource ".defaultvalue = $defval,"; WriteSource ".defaultvalueobjecttype = $defvalot,"; WriteSource ".defaultvalueattrid = $defvalattrid,"; WriteSource ".storedefaultvalue = $storedefaultval,"; WriteSource ".isenum = $isenum,"; WriteSource ".isenumlist = $isenumlist,"; WriteSource ".enummetadata = $enummetadata,"; WriteSource ".conditiontype = $conditiontype,"; WriteSource ".conditions = $conditions,"; WriteSource ".conditionslength = $conditionslen,"; WriteSource ".isconditional = ($conditionslen != 0),"; WriteSource ".validonlytype = $validonlytype,"; WriteSource ".validonly = $validonly,"; WriteSource ".validonlylength = $validonlylen,"; WriteSource ".isvalidonly = ($validonlylen != 0),"; WriteSource ".getsave = $getsave,"; WriteSource ".isvlan = $isvlan,"; WriteSource ".isaclfield = $isaclfield,"; WriteSource ".isaclaction = $isaclaction,"; WriteSource ".isaclmask = $isaclmask,"; WriteSource ".ismandatoryoncreate = $ismandatoryoncreate,"; WriteSource ".iscreateonly = $iscreateonly,"; WriteSource ".iscreateandset = $iscreateandset,"; WriteSource ".isreadonly = $isreadonly,"; WriteSource ".iskey = $iskey,"; WriteSource ".isprimitive = $isprimitive,"; WriteSource ".notificationtype = $ntftype,"; WriteSource ".iscallback = $iscallback,"; WriteSource ".pointertype = $ptrtype,"; WriteSource ".capability = $cap,"; WriteSource ".capabilitylength = $caplen,"; WriteSource ".isextensionattr = $isextensionattr,"; WriteSource ".isresourcetype = $isresourcetype,"; WriteSource ".isdeprecated = $isdeprecated,"; WriteSource ".isconditionrelaxed = $isrelaxed,"; WriteSource ".iscustom = ($attr >= 0x10000000) && ($attr < 0x20000000)"; WriteSource "};"; # check enum attributes if their names are ending on enum name CheckEnumNaming($attr, $meta{type}) if $isenum eq "true" or $isenumlist eq "true"; $MAX_CONDITIONS_LEN = $conditionslen if $MAX_CONDITIONS_LEN < $conditionslen; $MAX_CONDITIONS_LEN = $validonlylen if $MAX_CONDITIONS_LEN < $validonlylen; } } sub CheckEnumNaming { my ($attr, $type) = @_; LogError "can't match sai type on '$type'" if not $type =~ /.*sai_(\w+)_t/; my $enumTypeName = uc($1); return if $attr =~ /_${enumTypeName}_LIST$/; return if $attr =~ /_$enumTypeName$/; $attr =~ /SAI_(\w+?)_ATTR(_\w+?)(_LIST)?$/; my $attrObjectType = $1; my $attrSuffix = $2; if ($enumTypeName =~ /^${attrObjectType}_(\w+)$/) { my $enumTypeNameSuffix = $1; return if $attrSuffix =~ /_$enumTypeNameSuffix$/; LogError "enum starts by object type $attrObjectType but not ending on $enumTypeNameSuffix in $enumTypeName"; } LogError "$type == $attr not ending on enum name $enumTypeName"; } sub CreateMetadata { for my $key (sort keys %SAI_ENUMS) { next if not $key =~ /^(sai_(\w+)_attr_t)$/; my $typedef = $1; my $objtype = "SAI_OBJECT_TYPE_" . uc($2); ProcessSingleObjectType($typedef, $objtype); } } sub ProcessSaiStatus { my $filename = "../inc/saistatus.h"; open(my $fh, '<', $filename) or die "Could not open file '$filename' $!"; my @values = (); WriteSectionComment "Extra SAI statuses"; while (my $line = <$fh>) { next if not $line =~ /define\s+(SAI_STATUS_\w+).+(0x00\w+)/; my $status = $1; my $base = $2; push@values,$status; next if not ($status =~ /(SAI_\w+)_0$/); for my $idx (1..10) { $status = "$1_$idx"; WriteHeader "#define $status SAI_STATUS_CODE(($base + ${idx}))"; push@values,$status; } } close $fh; $SAI_ENUMS{"sai_status_t"}{values} = \@values; $SAI_ENUMS{"sai_status_t"}{flagsenum} = "true"; $SAI_ENUMS{"sai_status_t"}{flagstype} = "free"; } sub CreateMetadataForAttributes { my @objects = @{ $SAI_ENUMS{sai_object_type_t}{values} }; for my $ot (@objects) { if (not $ot =~ /^SAI_OBJECT_TYPE_(\w+)$/) { LogError "invalid object type '$ot'"; next; } my $type = "sai_" . lc($1) . "_attr_t"; if (not defined $SAI_ENUMS{$type}) { my @empty = (); $SAI_ENUMS{$type}{values} = \@empty; } WriteSource "const sai_attr_metadata_t* const sai_metadata_object_type_$type\[\] = {"; my @values = @{ $SAI_ENUMS{$type}{values} }; for my $value (@values) { next if defined $METADATA{$type}{$value}{ignore}; WriteSource "&sai_metadata_attr_$value,"; } WriteSource "NULL"; WriteSource "};"; } # This is disabled since it's object type can't be used as index any more # WriteHeader "extern const sai_attr_metadata_t* const* const sai_metadata_attr_by_object_type[];"; WriteSource "const sai_attr_metadata_t* const* const sai_metadata_attr_by_object_type[] = {"; for my $ot (@objects) { if (not $ot =~ /^SAI_OBJECT_TYPE_(\w+)$/) { LogError "invalid object type '$ot'"; next; } my $type = "sai_" . lc($1) . "_attr_t"; WriteSource "sai_metadata_object_type_$type,"; } WriteSource "NULL"; WriteSource "};"; my $count = @objects; WriteHeader "extern const size_t sai_metadata_attr_by_object_type_count;"; WriteSource "const size_t sai_metadata_attr_by_object_type_count = $count;"; } sub CreateEnumHelperMethod { my $key = shift; return if not $key =~ /^sai_(\w+)_t/; WriteSource "const char* sai_metadata_get_$1_name("; WriteSource "_In_ $key value)"; WriteSource "{"; WriteSource "return sai_metadata_get_enum_value_name(&sai_metadata_enum_$key, value);"; WriteSource "}"; WriteHeader "extern const char* sai_metadata_get_$1_name("; WriteHeader "_In_ $key value);\n"; } sub CreateEnumHelperMethods { WriteSectionComment "Get enum name helper methods"; for my $key (sort keys %SAI_ENUMS) { next if $key =~ /_attr_(extensions_)?t$/; CreateEnumHelperMethod($key); } } sub ProcessIsNonObjectId { my $struct = shift; return "false" if not defined $struct; return "true"; } sub ProcessStructValueType { my $type = shift; return "SAI_ATTR_VALUE_TYPE_OBJECT_ID" if $type eq "sai_object_id_t"; return "SAI_ATTR_VALUE_TYPE_MAC" if $type eq "sai_mac_t"; return "SAI_ATTR_VALUE_TYPE_IP_ADDRESS" if $type eq "sai_ip_address_t"; return "SAI_ATTR_VALUE_TYPE_IP_PREFIX" if $type eq "sai_ip_prefix_t"; return "SAI_ATTR_VALUE_TYPE_PRBS_RX_STATE" if $type eq "sai_prbs_rx_state_t"; return "SAI_ATTR_VALUE_TYPE_UINT16" if $type eq "sai_vlan_id_t"; return "SAI_ATTR_VALUE_TYPE_UINT32" if $type eq "sai_label_id_t"; return "SAI_ATTR_VALUE_TYPE_UINT32" if $type eq "uint32_t"; return "SAI_ATTR_VALUE_TYPE_UINT32" if $type eq "sai_uint32_t"; return "SAI_ATTR_VALUE_TYPE_INT32" if $type =~ /^sai_\w+_type_t$/; # enum return "SAI_ATTR_VALUE_TYPE_UINT32_RANGE" if $type eq "sai_u32_range_t"; return "SAI_ATTR_VALUE_TYPE_NAT_ENTRY_DATA" if $type eq "sai_nat_entry_data_t"; return "SAI_ATTR_VALUE_TYPE_ENCRYPT_KEY" if $type eq "sai_encrypt_key_t"; return "SAI_ATTR_VALUE_TYPE_AUTH_KEY" if $type eq "sai_auth_key_t"; return "SAI_ATTR_VALUE_TYPE_MACSEC_SAK" if $type eq "sai_macsec_sak_t"; return "SAI_ATTR_VALUE_TYPE_MACSEC_AUTH_KEY" if $type eq "sai_macsec_auth_key_t"; return "SAI_ATTR_VALUE_TYPE_MACSEC_SALT" if $type eq "sai_macsec_salt_t"; return "SAI_ATTR_VALUE_TYPE_BOOL" if $type eq "bool"; return "SAI_ATTR_VALUE_TYPE_IPV6" if $type eq "sai_ip6_t"; return "SAI_ATTR_VALUE_TYPE_UINT8" if $type eq "sai_uint8_t"; return "SAI_ATTR_VALUE_TYPE_UINT16" if $type eq "sai_uint16_t"; return "SAI_ATTR_VALUE_TYPE_UINT64" if $type eq "uint64_t"; return "SAI_ATTR_VALUE_TYPE_TWAMP_STATS_DATA" if $type eq "sai_twamp_session_stats_data_t"; return "SAI_ATTR_VALUE_TYPE_INT32" if defined $SAI_ENUMS{$type}; # enum return "-1" if $type eq "sai_fdb_entry_t"; return "-1" if $type eq "sai_nat_entry_t"; return "-1" if $type eq "sai_attribute_t*"; LogError "invalid struct member value type $type"; return -1; } sub ProcessStructIsVlan { my $type = shift; return "true" if $type eq "sai_vlan_id_t"; return "false"; } sub ProcessStructObjects { my ($rawname, $key, $struct) = @_; my $type = $struct->{type}; return "NULL" if not $type eq "sai_object_id_t" and not $type eq "sai_attribute_t*"; WriteSource "const sai_object_type_t sai_metadata_struct_member_sai_${rawname}_t_${key}_allowed_objects[] = {"; my $objects = $struct->{objects}; for my $obj (@{ $objects }) { WriteSource "$obj,"; } WriteSource "-1,"; WriteSource "};"; return "sai_metadata_struct_member_sai_${rawname}_t_${key}_allowed_objects"; } sub ProcessStructObjectLen { my ($rawname, $key, $struct) = @_; my $type = $struct->{type}; return 0 if not $type eq "sai_object_id_t" and not $type eq "sai_attribute_t*"; my @objects = @{ $struct->{objects} }; my $count = @objects; return $count; } sub ProcessStructEnumData { my $type = shift; return "&sai_metadata_enum_$type" if defined $SAI_ENUMS{$type}; return "&sai_metadata_enum_$type" if $type =~ /^sai_\w+_type_t$/; # enum return "NULL"; } sub ProcessStructIsEnum { my $type = shift; return "true" if defined $SAI_ENUMS{$type}; return "true" if $type =~ /^sai_\w+_type_t$/; # enum return "false"; } sub ProcessStructGetOid { my ($type, $key, $rawname, $any) = @_; return "NULL" if $type ne "sai_object_id_t"; my $fname = "sai_metadata_struct_member_get_sai_${rawname}_t_${key}"; return "NULL" if (defined $any); WriteSource "sai_object_id_t $fname("; WriteSource "_In_ const sai_object_meta_key_t *object_meta_key)"; WriteSource "{"; WriteSource "return object_meta_key->objectkey.key.${rawname}.${key};"; WriteSource "}"; return $fname; } sub ProcessStructSetOid { my ($type, $key, $rawname, $any) = @_; return "NULL" if $type ne "sai_object_id_t"; my $fname = "sai_metadata_struct_member_set_sai_${rawname}_t_${key}"; return "NULL" if (defined $any); WriteSource "void $fname("; WriteSource "_Inout_ sai_object_meta_key_t *object_meta_key,"; WriteSource "_In_ sai_object_id_t oid)"; WriteSource "{"; WriteSource "object_meta_key->objectkey.key.${rawname}.${key} = oid;"; WriteSource "}"; return $fname; } sub ProcessStructOffset { my ($type, $key, $rawname) = @_; return "offsetof(sai_${rawname}_t,$key)"; } sub ProcessStructSize { my ($type, $key, $rawname) = @_; return "sizeof($type)"; } sub ProcessStructMembers { my ($struct, $ot, $rawname, $any) = @_; return "NULL" if not defined $struct; my @keys = GetStructKeysInOrder($struct); if ($keys[0] ne "switch_id" and not defined $any) { LogError "switch_id is not first item in $rawname"; } for my $key (@keys) { my $valuetype = ProcessStructValueType($struct->{$key}{type}); my $isvlan = ProcessStructIsVlan($struct->{$key}{type}); my $objects = ProcessStructObjects($rawname, $key, $struct->{$key}); my $objectlen = ProcessStructObjectLen($rawname, $key, $struct->{$key}); my $isenum = ProcessStructIsEnum($struct->{$key}{type}); my $enumdata = ProcessStructEnumData($struct->{$key}{type}); my $getoid = ProcessStructGetOid($struct->{$key}{type}, $key, $rawname, $any); my $setoid = ProcessStructSetOid($struct->{$key}{type}, $key, $rawname, $any); my $offset = ProcessStructOffset($struct->{$key}{type}, $key, $rawname); my $size = ProcessStructSize($struct->{$key}{type}, $key, $rawname); WriteSource "const sai_struct_member_info_t sai_metadata_struct_member_sai_${rawname}_t_$key = {"; WriteSource ".membervaluetype = $valuetype,"; WriteSource ".membername = \"$key\","; WriteSource ".isvlan = $isvlan,"; WriteSource ".allowedobjecttypes = $objects,"; WriteSource ".allowedobjecttypeslength = $objectlen,"; WriteSource ".isenum = $isenum,"; WriteSource ".enummetadata = $enumdata,"; WriteSource ".getoid = $getoid,"; WriteSource ".setoid = $setoid,"; WriteSource ".offset = $offset,"; WriteSource ".size = $size,"; # TODO allow null WriteSource "};"; if ($objectlen > 0 and not $key =~ /_id$/ and not defined $any) { LogWarning "struct member key '$key' should end at _id in sai_${rawname}_t"; } } WriteSource "const sai_struct_member_info_t* const sai_metadata_struct_members_sai_${rawname}_t[] = {"; for my $key (@keys) { WriteSource "&sai_metadata_struct_member_sai_${rawname}_t_$key,"; } WriteSource "NULL"; WriteSource "};"; return "sai_metadata_struct_members_sai_${rawname}_t"; } sub ProcessStructMembersCount { my $struct = shift; return "0" if not defined $struct; my $count = keys %$struct; return $count; } sub ProcessRevGraph { # # Purpose of this method is to generate metadata where current object type # is used since currentrly if we have attribute metadata we can easly scan # attributes with oids values and extract information of object being used # on that attribute, scanning all attributes of that object type we have # dependency graph # # but what we create here is reverse dependency graph it will tell us on # which object and which attrubute current object type is used # # we can of course create both graphs right at the same time # my %REVGRAPH = GetReverseDependencyGraph(); my $objectType = shift; if (not defined $REVGRAPH{$objectType}) { # some objects are not used, so they will be not defined return "NULL"; } my @dep = @{ $REVGRAPH{$objectType} }; @dep = sort @dep; my $index = 0; my @membernames = ();; for my $dep (@dep) { my ($depObjectType, $attrId) = split/,/,$dep; my $membername = "sai_metadata_${objectType}_rev_graph_member_$index"; push@membernames,$membername; WriteSource "const sai_rev_graph_member_t $membername = {"; WriteSource ".objecttype = (sai_object_type_t)$objectType,"; WriteSource ".depobjecttype = (sai_object_type_t)$depObjectType,"; if ($attrId =~ /^SAI_\w+_ATTR_\w+/) { # this is attribute WriteSource ".attrmetadata = &sai_metadata_attr_$attrId,"; WriteSource ".structmember = NULL,"; } else { # this is struct member inside non object id struct my $DEPOT = lc ($1) if $depObjectType =~ /SAI_OBJECT_TYPE_(\w+)/; WriteSource ".attrmetadata = NULL,"; WriteSource ".structmember = &sai_metadata_struct_member_sai_${DEPOT}_t_$attrId,"; } WriteSource "};"; $index++; } WriteSource "const sai_rev_graph_member_t* const sai_metadata_${objectType}_rev_graph_members[] = {"; for my $mn (@membernames) { WriteSource "&$mn,"; } WriteSource "NULL"; WriteSource "};"; return "sai_metadata_${objectType}_rev_graph_members"; } sub ProcessRevGraphCount { my $objectType = shift; return 0 if not defined $REVGRAPH{$objectType}; return scalar @{ $REVGRAPH{$objectType} }; } sub CreateStructNonObjectId { my @objects = @{ $SAI_ENUMS{sai_object_type_t}{values} }; for my $ot (@objects) { if (not $ot =~ /^SAI_OBJECT_TYPE_(\w+)$/) { LogError "invalid object type '$ot'"; next; } next if $1 eq "NULL" or $1 eq "MAX"; my $type = "sai_" . lc($1) . "_attr_t"; my $enum = "&sai_metadata_enum_${type}"; my $struct = $NON_OBJECT_ID_STRUCTS{$ot}; my $structmembers = ProcessStructMembers($struct, $ot ,lc($1)); } } sub ProcessStructMembersName { my ($struct, $ot, $rawname) = @_; return "NULL" if not defined $struct; return "sai_metadata_struct_members_sai_${rawname}_t"; } sub ProcessCreate { my $struct = shift; my $ot = shift; my $small = lc($1) if $ot =~ /SAI_OBJECT_TYPE_(\w+)/; my $api = $OBJTOAPIMAP{$ot}; WriteSource "sai_status_t sai_metadata_generic_create_$ot("; WriteSource "_Inout_ sai_object_meta_key_t *meta_key,"; WriteSource "_In_ sai_object_id_t switch_id,"; WriteSource "_In_ uint32_t attr_count,"; WriteSource "_In_ const sai_attribute_t *attr_list)"; WriteSource "{"; if (IsSpecialObject($ot)) { WriteSource "return SAI_STATUS_NOT_IMPLEMENTED;"; } elsif (not defined $struct) { if ($small eq "switch") { WriteSource "return sai_metadata_sai_${api}_api->create_$small(&meta_key->objectkey.key.object_id, attr_count, attr_list);"; } else { WriteSource "return sai_metadata_sai_${api}_api->create_$small(&meta_key->objectkey.key.object_id, switch_id, attr_count, attr_list);"; } } else { WriteSource "return sai_metadata_sai_${api}_api->create_$small(&meta_key->objectkey.key.$small, attr_count, attr_list);"; } WriteSource "}"; return "sai_metadata_generic_create_$ot"; } sub ProcessRemove { my $struct = shift; my $ot = shift; my $small = lc($1) if $ot =~ /SAI_OBJECT_TYPE_(\w+)/; my $api = $OBJTOAPIMAP{$ot}; WriteSource "sai_status_t sai_metadata_generic_remove_$ot("; WriteSource "_In_ const sai_object_meta_key_t *meta_key)"; WriteSource "{"; if (IsSpecialObject($ot)) { WriteSource "return SAI_STATUS_NOT_IMPLEMENTED;"; } elsif (not defined $struct) { WriteSource "return sai_metadata_sai_${api}_api->remove_$small(meta_key->objectkey.key.object_id);"; } else { WriteSource "return sai_metadata_sai_${api}_api->remove_$small(&meta_key->objectkey.key.$small);"; } WriteSource "}"; return "sai_metadata_generic_remove_$ot"; } sub ProcessSet { my $struct = shift; my $ot = shift; my $small = lc($1) if $ot =~ /SAI_OBJECT_TYPE_(\w+)/; my $api = $OBJTOAPIMAP{$ot}; WriteSource "sai_status_t sai_metadata_generic_set_$ot("; WriteSource "_In_ const sai_object_meta_key_t *meta_key,"; WriteSource "_In_ const sai_attribute_t *attr)"; WriteSource "{"; if (IsSpecialObject($ot)) { WriteSource "return SAI_STATUS_NOT_IMPLEMENTED;"; } elsif (not defined $struct) { WriteSource "return sai_metadata_sai_${api}_api->set_${small}_attribute(meta_key->objectkey.key.object_id, attr);"; } else { WriteSource "return sai_metadata_sai_${api}_api->set_${small}_attribute(&meta_key->objectkey.key.$small, attr);"; } WriteSource "}"; return "sai_metadata_generic_set_$ot"; } sub ProcessGet { my $struct = shift; my $ot = shift; my $small = lc($1) if $ot =~ /SAI_OBJECT_TYPE_(\w+)/; my $api = $OBJTOAPIMAP{$ot}; WriteSource "sai_status_t sai_metadata_generic_get_$ot("; WriteSource "_In_ const sai_object_meta_key_t *meta_key,"; WriteSource "_In_ uint32_t attr_count,"; WriteSource "_Inout_ sai_attribute_t *attr_list)"; WriteSource "{"; if (IsSpecialObject($ot)) { WriteSource "return SAI_STATUS_NOT_IMPLEMENTED;"; } elsif (not defined $struct) { WriteSource "if (!sai_metadata_sai_${api}_api || !sai_metadata_sai_${api}_api->get_${small}_attribute)"; WriteSource "{"; WriteSource "return SAI_STATUS_NOT_SUPPORTED;"; WriteSource "}"; WriteSource "return sai_metadata_sai_${api}_api->get_${small}_attribute(meta_key->objectkey.key.object_id, attr_count, attr_list);"; } else { WriteSource "if (!sai_metadata_sai_${api}_api || !sai_metadata_sai_${api}_api->get_${small}_attribute)"; WriteSource "{"; WriteSource "return SAI_STATUS_NOT_SUPPORTED;"; WriteSource "}"; WriteSource "return sai_metadata_sai_${api}_api->get_${small}_attribute(&meta_key->objectkey.key.$small, attr_count, attr_list);"; } WriteSource "}"; return "sai_metadata_generic_get_$ot"; } sub ProcessGetStats { my $struct = shift; my $ot = shift; my $small = lc($1) if $ot =~ /SAI_OBJECT_TYPE_(\w+)/; my $api = $OBJTOAPIMAP{$ot}; WriteSource "sai_status_t sai_metadata_generic_get_stats_$ot("; WriteSource "_In_ const sai_object_meta_key_t *meta_key,"; WriteSource "_In_ uint32_t number_of_counters,"; WriteSource "_In_ const sai_stat_id_t *counter_ids,"; WriteSource "_Out_ uint64_t *counters)"; WriteSource "{"; if (IsSpecialObject($ot) or not defined $OBJECT_TYPE_TO_STATS_MAP{$small}) { WriteSource "return SAI_STATUS_NOT_SUPPORTED;"; } elsif (not defined $struct) { WriteSource "return sai_metadata_sai_${api}_api->get_${small}_stats(meta_key->objectkey.key.object_id, number_of_counters, counter_ids, counters);"; } else { WriteSource "return sai_metadata_sai_${api}_api->get_${small}_stats(&meta_key->objectkey.key.$small, number_of_counters, counter_ids, counters);"; } WriteSource "}"; return "sai_metadata_generic_get_stats_$ot"; } sub ProcessGetStatsExt { my $struct = shift; my $ot = shift; my $small = lc($1) if $ot =~ /SAI_OBJECT_TYPE_(\w+)/; my $api = $OBJTOAPIMAP{$ot}; WriteSource "sai_status_t sai_metadata_generic_get_stats_ext_$ot("; WriteSource "_In_ const sai_object_meta_key_t *meta_key,"; WriteSource "_In_ uint32_t number_of_counters,"; WriteSource "_In_ const sai_stat_id_t *counter_ids,"; WriteSource "_In_ sai_stats_mode_t mode,"; WriteSource "_Out_ uint64_t *counters)"; WriteSource "{"; if (IsSpecialObject($ot) or not defined $OBJECT_TYPE_TO_STATS_MAP{$small}) { WriteSource "return SAI_STATUS_NOT_SUPPORTED;"; } elsif (not defined $struct) { WriteSource "return sai_metadata_sai_${api}_api->get_${small}_stats_ext(meta_key->objectkey.key.object_id, number_of_counters, counter_ids, mode, counters);"; } else { WriteSource "return sai_metadata_sai_${api}_api->get_${small}_stats_ext(&meta_key->objectkey.key.$small, number_of_counters, counter_ids, mode, counters);"; } WriteSource "}"; return "sai_metadata_generic_get_stats_ext_$ot"; } sub ProcessClearStats { my $struct = shift; my $ot = shift; my $small = lc($1) if $ot =~ /SAI_OBJECT_TYPE_(\w+)/; my $api = $OBJTOAPIMAP{$ot}; WriteSource "sai_status_t sai_metadata_generic_clear_stats_$ot("; WriteSource "_In_ const sai_object_meta_key_t *meta_key,"; WriteSource "_In_ uint32_t number_of_counters,"; WriteSource "_In_ const sai_stat_id_t *counter_ids)"; WriteSource "{"; if (IsSpecialObject($ot) or not defined $OBJECT_TYPE_TO_STATS_MAP{$small}) { WriteSource "return SAI_STATUS_NOT_SUPPORTED;"; } elsif (not defined $struct) { WriteSource "return sai_metadata_sai_${api}_api->clear_${small}_stats(meta_key->objectkey.key.object_id, number_of_counters, counter_ids);"; } else { WriteSource "return sai_metadata_sai_${api}_api->clear_${small}_stats(&meta_key->objectkey.key.$small, number_of_counters, counter_ids);"; } WriteSource "}"; return "sai_metadata_generic_clear_stats_$ot"; } sub CreateApis { WriteSectionComment "Global SAI API declarations"; for my $key (sort keys %APITOOBJMAP) { WriteSource "sai_${key}_api_t *sai_metadata_sai_${key}_api = NULL;"; WriteHeader "extern sai_${key}_api_t *sai_metadata_sai_${key}_api;"; } } sub CreateApisStruct { my @apis = @{ $SAI_ENUMS{sai_api_t}{values} }; WriteSectionComment "All APIs struct"; WriteHeader "typedef struct _sai_apis_t {"; for my $api (@apis) { $api =~ /^SAI_API_(\w+)/; $api = lc($1); next if $api =~ /unspecified/; WriteHeader "sai_${api}_api_t* ${api}_api;"; } WriteHeader "} sai_apis_t;"; my $count = scalar @apis; } sub CreateGlobalApis { WriteSectionComment "Global SAI API declarations"; WriteSource "sai_apis_t sai_metadata_apis = { 0 };"; WriteHeader "extern sai_apis_t sai_metadata_apis;"; } sub CreateGlobalFunctions { WriteSectionComment "Global functions"; for my $name (sort keys %GLOBAL_APIS) { my $type = $GLOBAL_APIS{$name}{type}; my $args = $GLOBAL_APIS{$name}{args}; $args =~ s/(_(In|Out|Inout)_)/\n $1/g; $args =~ s/\s+$//mg; WriteHeader "typedef $type (*${name}_fn) $args;"; WriteHeader ""; } WriteHeader "typedef struct _sai_global_apis_t {"; for my $name (sort keys %GLOBAL_APIS) { my $short = $1 if $name =~ /^sai_(\w+)/; WriteHeader "${name}_fn $short;"; } WriteHeader "} sai_global_apis_t;"; WriteHeader ""; my $typename = "sai_global_api_type_t"; my $prefix = uc $typename; chop $prefix; WriteHeader "typedef enum _$typename {"; my @values = (); for my $name (sort keys %GLOBAL_APIS) { my $short = uc($1) if $name =~ /^sai_(\w+)/; WriteHeader "SAI_GLOBAL_API_TYPE_$short,"; push @values, "SAI_GLOBAL_API_TYPE_$short"; } WriteHeader "} $typename;"; $SAI_ENUMS{$typename}{values} = \@values; WriteSectionComment "$typename metadata"; ProcessSingleEnum($typename, $typename, $prefix); WriteSectionComment "Get $typename helper method"; CreateEnumHelperMethod($typename); } sub ProcessGenericQuadApi { my $name = shift; my $params = shift; WriteSource "switch((int)meta_key->objecttype)"; WriteSource "{"; WriteSource "case SAI_OBJECT_TYPE_NULL:"; WriteSource " return SAI_STATUS_NOT_SUPPORTED;"; my @objects = @{ $SAI_ENUMS{sai_object_type_t}{values} }; for my $ot (@objects) { if (not $ot =~ /^SAI_OBJECT_TYPE_(\w+)$/) { LogError "invalid object type '$ot'"; next; } next if $1 eq "NULL" or $1 eq "MAX"; if (not defined $OBJTOAPIMAP{$ot}) { LogError "$ot is not defined in OBJTOAPIMAP, missing sai_XXX_api_t declaration?"; next; } my $struct = $NON_OBJECT_ID_STRUCTS{$ot}; my $small = lc($1) if $ot =~ /SAI_OBJECT_TYPE_(\w+)/; my $api = $OBJTOAPIMAP{$ot}; my $amp = ($name eq "create") ? "&" : ""; my $attr = ($name eq "set" or $name eq "get") ? "_attribute" : ""; if (IsSpecialObject($ot)) { WriteSource "case $ot:"; WriteSource " return SAI_STATUS_NOT_SUPPORTED;"; } elsif (not defined $struct) { my $param = $params; $param =~ s/switch_id,// if $small eq "switch"; WriteSource "case $ot:"; WriteSource " return (apis->${api}_api && apis->${api}_api->${name}_${small}${attr})"; WriteSource " ? apis->${api}_api->${name}_${small}${attr}(${amp}meta_key->objectkey.key.object_id${param})"; WriteSource " : SAI_STATUS_NOT_IMPLEMENTED;"; } else { my $param = $params; $param =~ s/switch_id,//; WriteSource "case $ot:"; WriteSource " return (apis->${api}_api && apis->${api}_api->${name}_${small}${attr})"; WriteSource " ? apis->${api}_api->${name}_${small}${attr}(&meta_key->objectkey.key.$small${param})"; WriteSource " : SAI_STATUS_NOT_IMPLEMENTED;"; } } WriteSource "default:"; WriteSource " SAI_META_LOG_NOTICE(\"object type %d not implemented\", meta_key->objecttype);"; WriteSource " return SAI_STATUS_NOT_IMPLEMENTED;"; WriteSource "}"; } sub CreateGenericQuadApi { WriteSectionComment "Generic Quad API"; WriteHeader "sai_status_t sai_metadata_generic_create("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _Inout_ sai_object_meta_key_t *meta_key,"; WriteHeader " _In_ sai_object_id_t switch_id,"; WriteHeader " _In_ uint32_t attr_count,"; WriteHeader " _In_ const sai_attribute_t *attr_list);"; WriteHeader ""; WriteHeader "sai_status_t sai_metadata_generic_remove("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _In_ const sai_object_meta_key_t *meta_key);"; WriteHeader ""; WriteHeader "sai_status_t sai_metadata_generic_set("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _In_ const sai_object_meta_key_t *meta_key,"; WriteHeader " _In_ const sai_attribute_t *attr);"; WriteHeader ""; WriteHeader "sai_status_t sai_metadata_generic_get("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _In_ const sai_object_meta_key_t *meta_key,"; WriteHeader " _In_ uint32_t attr_count,"; WriteHeader " _Inout_ sai_attribute_t *attr_list);"; WriteHeader ""; # actual implementation WriteSource "sai_status_t sai_metadata_generic_create("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _Inout_ sai_object_meta_key_t *meta_key,"; WriteSource " _In_ sai_object_id_t switch_id,"; WriteSource " _In_ uint32_t attr_count,"; WriteSource " _In_ const sai_attribute_t *attr_list)"; WriteSource "{"; ProcessGenericQuadApi("create", ", switch_id, attr_count, attr_list"); WriteSource "}"; WriteSource "sai_status_t sai_metadata_generic_remove("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _In_ const sai_object_meta_key_t *meta_key)"; WriteSource "{"; ProcessGenericQuadApi("remove",""); WriteSource "}"; WriteSource "sai_status_t sai_metadata_generic_set("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _In_ const sai_object_meta_key_t *meta_key,"; WriteSource " _In_ const sai_attribute_t *attr)"; WriteSource "{"; ProcessGenericQuadApi("set", ", attr"); WriteSource "}"; WriteSource "sai_status_t sai_metadata_generic_get("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _In_ const sai_object_meta_key_t *meta_key,"; WriteSource " _In_ uint32_t attr_count,"; WriteSource " _Inout_ sai_attribute_t *attr_list)"; WriteSource "{"; ProcessGenericQuadApi("get", ", attr_count, attr_list"); WriteSource "}"; } sub ProcessGenericStatsApi { my $name = shift; my $suffix= shift; my $params = shift; WriteSource "switch((int)meta_key->objecttype)"; WriteSource "{"; WriteSource "case SAI_OBJECT_TYPE_NULL:"; WriteSource " return SAI_STATUS_NOT_SUPPORTED;"; my @objects = @{ $SAI_ENUMS{sai_object_type_t}{values} }; for my $ot (@objects) { if (not $ot =~ /^SAI_OBJECT_TYPE_(\w+)$/) { LogError "invalid object type '$ot'"; next; } next if $1 eq "NULL" or $1 eq "MAX"; if (not defined $OBJTOAPIMAP{$ot}) { LogError "$ot is not defined in OBJTOAPIMAP, missing sai_XXX_api_t declaration?"; next; } my $struct = $NON_OBJECT_ID_STRUCTS{$ot}; my $small = lc($1) if $ot =~ /SAI_OBJECT_TYPE_(\w+)/; my $api = $OBJTOAPIMAP{$ot}; if (IsSpecialObject($ot) or not defined $OBJECT_TYPE_TO_STATS_MAP{$small}) { WriteSource "case $ot:"; WriteSource " return SAI_STATUS_NOT_SUPPORTED;"; } elsif (not defined $struct) { WriteSource "case $ot:"; WriteSource " return (apis->${api}_api && apis->${api}_api->${name}_${small}_stats${suffix})"; WriteSource " ? apis->${api}_api->${name}_${small}_stats${suffix}(meta_key->objectkey.key.object_id, ${params})"; WriteSource " : SAI_STATUS_NOT_IMPLEMENTED;"; } else { WriteSource "case $ot:"; WriteSource " return (apis->${api}_api && apis->${api}_api->${name}_${small}_stats${suffix})"; WriteSource " ? apis->${api}_api->${name}_${small}_stats${suffix}(&meta_key->objectkey.key.$small, ${params})"; WriteSource " : SAI_STATUS_NOT_IMPLEMENTED;"; } } WriteSource "default:"; WriteSource " SAI_META_LOG_NOTICE(\"object type %d not implemented\", meta_key->objecttype);"; WriteSource " return SAI_STATUS_NOT_IMPLEMENTED;"; WriteSource "}"; } sub CreateGenericStatsApi { WriteSectionComment "Generic Stats API"; WriteHeader "sai_status_t sai_metadata_generic_get_stats("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _In_ const sai_object_meta_key_t *meta_key,"; WriteHeader " _In_ uint32_t number_of_counters,"; WriteHeader " _In_ const sai_stat_id_t *counter_ids,"; WriteHeader " _Out_ uint64_t *counters);"; WriteHeader ""; WriteHeader "sai_status_t sai_metadata_generic_get_stats_ext("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _In_ const sai_object_meta_key_t *meta_key,"; WriteHeader " _In_ uint32_t number_of_counters,"; WriteHeader " _In_ const sai_stat_id_t *counter_ids,"; WriteHeader " _In_ sai_stats_mode_t mode,"; WriteHeader " _Out_ uint64_t *counters);"; WriteHeader ""; WriteHeader "sai_status_t sai_metadata_generic_clear_stats("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _In_ const sai_object_meta_key_t *meta_key,"; WriteHeader " _In_ uint32_t number_of_counters,"; WriteHeader " _In_ const sai_stat_id_t *counter_ids);"; WriteHeader ""; # actual implementation WriteSource "sai_status_t sai_metadata_generic_get_stats("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _In_ const sai_object_meta_key_t *meta_key,"; WriteSource " _In_ uint32_t number_of_counters,"; WriteSource " _In_ const sai_stat_id_t *counter_ids,"; WriteSource " _Out_ uint64_t *counters)"; WriteSource "{"; ProcessGenericStatsApi("get", "", "number_of_counters, counter_ids, counters"); WriteSource "}"; WriteSource "sai_status_t sai_metadata_generic_get_stats_ext("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _In_ const sai_object_meta_key_t *meta_key,"; WriteSource " _In_ uint32_t number_of_counters,"; WriteSource " _In_ const sai_stat_id_t *counter_ids,"; WriteSource " _In_ sai_stats_mode_t mode,"; WriteSource " _Out_ uint64_t *counters)"; WriteSource "{"; ProcessGenericStatsApi("get", "_ext", "number_of_counters, counter_ids, mode, counters"); WriteSource "}"; WriteSource "sai_status_t sai_metadata_generic_clear_stats("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _In_ const sai_object_meta_key_t *meta_key,"; WriteSource " _In_ uint32_t number_of_counters,"; WriteSource " _In_ const sai_stat_id_t *counter_ids)"; WriteSource "{"; ProcessGenericStatsApi("clear", "", "number_of_counters, counter_ids"); WriteSource "}"; } sub ProcessGenericQuadBulkApi { my $name = shift; my $params = shift; WriteSource "switch((int)meta_key->objecttype)"; WriteSource "{"; WriteSource "case SAI_OBJECT_TYPE_NULL:"; WriteSource " return SAI_STATUS_NOT_SUPPORTED;"; my @objects = @{ $SAI_ENUMS{sai_object_type_t}{values} }; for my $ot (@objects) { if (not $ot =~ /^SAI_OBJECT_TYPE_(\w+)$/) { LogError "invalid object type '$ot'"; next; } next if $1 eq "NULL" or $1 eq "MAX"; if (not defined $OBJTOAPIMAP{$ot}) { LogError "$ot is not defined in OBJTOAPIMAP, missing sai_XXX_api_t declaration?"; next; } if (not defined $OBJECT_TYPE_BULK_MAP{$ot} or not defined $OBJECT_TYPE_BULK_MAP{$ot}{$name}) { WriteSource "case $ot:"; WriteSource " return SAI_STATUS_NOT_SUPPORTED;"; next; } my $struct = $NON_OBJECT_ID_STRUCTS{$ot}; my $small = lc($1) if $ot =~ /SAI_OBJECT_TYPE_(\w+)/; my $api = $OBJTOAPIMAP{$ot}; if (IsSpecialObject($ot)) { WriteSource "case $ot:"; WriteSource " return SAI_STATUS_NOT_SUPPORTED;"; } elsif (not defined $struct) { WriteSource "case $ot:"; WriteSource "{"; WriteSource "sai_object_id_t* objects = calloc(object_count, sizeof(sai_object_id_t));"; WriteSource "uint32_t i;"; WriteSource "sai_status_t status = SAI_STATUS_NOT_IMPLEMENTED;"; WriteSource "for (i = 0; i < object_count; i++)"; WriteSource "{"; WriteSource "objects[i] = meta_key[i].objectkey.key.object_id;"; WriteSource "}"; my $f = ($name =~ /set|get/) ? "${name}_${small}s_attribute" : "${name}_${small}s"; my $p = ($name eq "create") ? "switch_id, object_count, attr_count, attr_list, mode, objects, object_statuses" : $params; WriteSource "status = (apis->${api}_api && apis->${api}_api->${f})"; WriteSource " ? apis->${api}_api->${f}($p)"; WriteSource " : SAI_STATUS_NOT_IMPLEMENTED;"; if ($name eq "create") { WriteSource "for (i = 0; i < object_count; i++)"; WriteSource "{"; WriteSource "meta_key[i].objectkey.key.object_id = objects[i];"; WriteSource "}"; } WriteSource "free(objects);"; WriteSource "return status;"; WriteSource "}"; } else { WriteSource "case $ot:"; WriteSource "{"; WriteSource "sai_${small}_t* objects = calloc(object_count, sizeof(sai_${small}_t));"; WriteSource "uint32_t i;"; WriteSource "sai_status_t status = SAI_STATUS_NOT_IMPLEMENTED;"; WriteSource "for (i = 0; i < object_count; i++)"; WriteSource "{"; WriteSource "objects[i] = meta_key[i].objectkey.key.${small};"; WriteSource "}"; my $f = ($name =~ /set|get/) ? "${name}_${small}s_attribute" : "${name}_${small}s"; $f =~ s/entrys/entries/; my $p = $params; $p =~ s/switch_id,// if $name eq "create"; WriteSource "status = (apis->${api}_api && apis->${api}_api->${f})"; WriteSource " ? apis->${api}_api->${f}($p)"; WriteSource " : SAI_STATUS_NOT_IMPLEMENTED;"; WriteSource "free(objects);"; WriteSource "return status;"; WriteSource "}"; } } WriteSource "default:"; WriteSource " SAI_META_LOG_NOTICE(\"object type %d not implemented\", meta_key->objecttype);"; WriteSource " return SAI_STATUS_NOT_IMPLEMENTED;"; WriteSource "}"; } sub CreateGenericQuadBulkApi { WriteSectionComment "Generic Quad Bulk API"; WriteHeader "sai_status_t sai_metadata_generic_bulk_create("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _In_ sai_object_id_t switch_id,"; WriteHeader " _In_ uint32_t object_count,"; WriteHeader " _Inout_ sai_object_meta_key_t *meta_key,"; WriteHeader " _In_ const uint32_t *attr_count,"; WriteHeader " _In_ const sai_attribute_t **attr_list,"; WriteHeader " _In_ sai_bulk_op_error_mode_t mode,"; WriteHeader " _Out_ sai_status_t *object_statuses);"; WriteHeader ""; WriteHeader "sai_status_t sai_metadata_generic_bulk_remove("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _In_ uint32_t object_count,"; WriteHeader " _In_ const sai_object_meta_key_t *meta_key,"; WriteHeader " _In_ sai_bulk_op_error_mode_t mode,"; WriteHeader " _Out_ sai_status_t *object_statuses);"; WriteHeader ""; WriteHeader "sai_status_t sai_metadata_generic_bulk_set("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _In_ uint32_t object_count,"; WriteHeader " _In_ const sai_object_meta_key_t *meta_key,"; WriteHeader " _In_ const sai_attribute_t *attr_list,"; WriteHeader " _In_ sai_bulk_op_error_mode_t mode,"; WriteHeader " _Out_ sai_status_t *object_statuses);"; WriteHeader ""; WriteHeader "sai_status_t sai_metadata_genecic_bulk_get("; WriteHeader " _In_ const sai_apis_t* apis,"; WriteHeader " _In_ uint32_t object_count,"; WriteHeader " _In_ const sai_object_meta_key_t *meta_key,"; WriteHeader " _In_ const uint32_t *attr_count,"; WriteHeader " _Inout_ sai_attribute_t **attr_list,"; WriteHeader " _In_ sai_bulk_op_error_mode_t mode,"; WriteHeader " _Out_ sai_status_t *object_statuses);"; WriteHeader ""; # actual implementation WriteSource "sai_status_t sai_metadata_generic_bulk_create("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _In_ sai_object_id_t switch_id,"; WriteSource " _In_ uint32_t object_count,"; WriteSource " _Inout_ sai_object_meta_key_t *meta_key,"; WriteSource " _In_ const uint32_t *attr_count,"; WriteSource " _In_ const sai_attribute_t **attr_list,"; WriteSource " _In_ sai_bulk_op_error_mode_t mode,"; WriteSource " _Out_ sai_status_t *object_statuses)"; WriteSource "{"; ProcessGenericQuadBulkApi("create", "switch_id, object_count, objects, attr_count, attr_list, mode, object_statuses"); WriteSource "}"; WriteSource "sai_status_t sai_metadata_generic_bulk_remove("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _In_ uint32_t object_count,"; WriteSource " _In_ const sai_object_meta_key_t *meta_key,"; WriteSource " _In_ sai_bulk_op_error_mode_t mode,"; WriteSource " _Out_ sai_status_t *object_statuses)"; WriteSource "{"; ProcessGenericQuadBulkApi("remove", "object_count, objects, mode, object_statuses"); WriteSource "}"; WriteSource "sai_status_t sai_metadata_generic_bulk_set("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _In_ uint32_t object_count,"; WriteSource " _In_ const sai_object_meta_key_t *meta_key,"; WriteSource " _In_ const sai_attribute_t *attr_list,"; WriteSource " _In_ sai_bulk_op_error_mode_t mode,"; WriteSource " _Out_ sai_status_t *object_statuses)"; WriteSource "{"; ProcessGenericQuadBulkApi("set", "object_count, objects, attr_list, mode, object_statuses"); WriteSource "}"; WriteSource "sai_status_t sai_metadata_genecic_bulk_get("; WriteSource " _In_ const sai_apis_t* apis,"; WriteSource " _In_ uint32_t object_count,"; WriteSource " _In_ const sai_object_meta_key_t *meta_key,"; WriteSource " _In_ const uint32_t *attr_count,"; WriteSource " _Inout_ sai_attribute_t **attr_list,"; WriteSource " _In_ sai_bulk_op_error_mode_t mode,"; WriteSource " _Out_ sai_status_t *object_statuses)"; WriteSource "{"; ProcessGenericQuadBulkApi("get", "object_count, objects, attr_count, attr_list, mode, object_statuses"); WriteSource "}"; } sub CreateApisQuery { WriteSectionComment "SAI API query"; # for switch we need to generate wrapper, for others we can use pointers # so we don't need to use meta key then WriteSource "int sai_metadata_apis_query("; WriteSource "_In_ const sai_api_query_fn api_query,"; WriteSource "_Inout_ sai_apis_t *apis)"; WriteSource "{"; WriteSource "sai_status_t status = SAI_STATUS_SUCCESS;"; WriteSource "int count = 0;"; WriteSource "if (api_query == NULL)"; WriteSource "{"; for my $key (sort keys %APITOOBJMAP) { WriteSource "sai_metadata_sai_${key}_api = NULL;"; } WriteSource "memset(apis, 0, sizeof(sai_apis_t));"; WriteSource "memset(&sai_metadata_apis, 0, sizeof(sai_apis_t));"; WriteSource "return count;"; WriteSource "}"; for my $key (sort keys %APITOOBJMAP) { my $api = uc("SAI_API_${key}"); WriteSource "status = api_query($api, (void**)&sai_metadata_sai_${key}_api);"; WriteSource "apis->${key}_api = sai_metadata_sai_${key}_api;"; WriteSource "if (status != SAI_STATUS_SUCCESS)"; WriteSource "{"; WriteSource "count++;"; WriteSource "const char *name = sai_metadata_get_enum_value_name(&sai_metadata_enum_sai_status_t, status);"; WriteSource "SAI_META_LOG_NOTICE(\"failed to query api $api: %s (%d)\", name, status);"; WriteSource "}"; } WriteSource "return count; /* number of unsuccesfull apis */"; WriteSource "}"; WriteHeader "extern int sai_metadata_apis_query("; WriteHeader "_In_ const sai_api_query_fn api_query,"; WriteHeader "_Inout_ sai_apis_t *apis);"; } sub CreateGlobalApisQuery { WriteSectionComment "SAI global API query"; WriteHeader "typedef void* (*sai_dlsym_fn) (void * handle, const char* name);"; WriteHeader "typedef char* (*sai_dlerror_fn) (void);"; # TODO we could not pass handle and functions, but load functions internally # and just return void* as handle WriteHeader "extern sai_status_t sai_metadata_global_apis_query("; WriteHeader " _Inout_ sai_global_apis_t* global_apis,"; WriteHeader " _In_ void* handle,"; WriteHeader " _In_ const sai_dlsym_fn sym,"; WriteHeader " _In_ const sai_dlerror_fn error);"; WriteSource "int sai_metadata_global_apis_query("; WriteSource " _Inout_ sai_global_apis_t* global_apis,"; WriteSource " _In_ void* handle,"; WriteSource " _In_ const sai_dlsym_fn dlsym,"; WriteSource " _In_ const sai_dlerror_fn dlerror)"; WriteSource "{"; WriteSource "char* error;"; WriteSource "dlerror();"; for my $name (sort keys %GLOBAL_APIS) { my $short = $1 if $name =~ /^sai_(\w+)/; WriteSource "global_apis->$short = 0;"; } for my $name (sort keys %GLOBAL_APIS) { my $short = $1 if $name =~ /^sai_(\w+)/; WriteSource "*(void **) (&global_apis->$short) = dlsym(handle, \"sai_${short}\");"; WriteSource "if ((error = dlerror()) != NULL)"; WriteSource "{"; WriteSource "SAI_META_LOG_NOTICE(\"dlsym failed on sai_$short: %s\", error);"; WriteSource "}"; } WriteSource "return SAI_STATUS_SUCCESS;"; WriteSource "}"; } sub ProcessIsExperimental { my $ot = shift; return "true" if defined $EXPERIMENTAL_OBJECTS{$ot}; return "false"; } sub ProcessStatEnum { my $shortot = shift; my $statenumname = "sai_${shortot}_stat_t"; return "&sai_metadata_enum_$statenumname" if defined $SAI_ENUMS{$statenumname}; return "NULL"; } sub CreateObjectInfo { WriteSectionComment "Object info metadata"; %REVGRAPH = GetReverseDependencyGraph(); my @objects = @{ $SAI_ENUMS{sai_object_type_t}{values} }; for my $ot (@objects) { if (not $ot =~ /^SAI_OBJECT_TYPE_(\w+)$/) { LogError "invalid object type '$ot'"; next; } next if $1 eq "NULL" or $1 eq "MAX"; if (not defined $OBJTOAPIMAP{$ot}) { LogError "$ot is not defined in OBJTOAPIMAP, missing sai_XXX_api_t declaration?"; next; } my $shortot = lc($1); my $type = "sai_" . lc($1) . "_attr_t"; my $start = "SAI_" . uc($1) . "_ATTR_START"; my $end = "SAI_" . uc($1) . "_ATTR_END"; my $enum = "&sai_metadata_enum_${type}"; my $struct = $NON_OBJECT_ID_STRUCTS{$ot}; # # here we need to only generate struct member names # since we use those members in rev graph entries # so struct members must be generated previously # my $isnonobjectid = ProcessIsNonObjectId($struct, $ot); my $structmembers = ProcessStructMembersName($struct, $ot ,lc($1)); my $structmemberscount = ProcessStructMembersCount($struct, $ot); my $revgraph = ProcessRevGraph($ot); my $revgraphcount = ProcessRevGraphCount($ot); my $isexperimental = ProcessIsExperimental($ot); my $statenum = ProcessStatEnum($shortot); my $attrmetalength = @{ $SAI_ENUMS{$type}{values} }; my $create = ProcessCreate($struct, $ot); my $remove = ProcessRemove($struct, $ot); my $set = ProcessSet($struct, $ot); my $get = ProcessGet($struct, $ot); my $getstats = ProcessGetStats($struct, $ot); my $getstatsext = ProcessGetStatsExt($struct, $ot); my $clearstats = ProcessClearStats($struct, $ot); WriteHeader "extern const sai_object_type_info_t sai_metadata_object_type_info_$ot;"; WriteSource "const sai_object_type_info_t sai_metadata_object_type_info_$ot = {"; WriteSource ".objecttype = (sai_object_type_t)$ot,"; WriteSource ".objecttypename = \"$ot\","; WriteSource ".attridstart = $start,"; WriteSource ".attridend = $end,"; WriteSource ".enummetadata = $enum,"; WriteSource ".attrmetadata = sai_metadata_object_type_$type,"; WriteSource ".attrmetadatalength = $attrmetalength,"; WriteSource ".isnonobjectid = $isnonobjectid,"; WriteSource ".isobjectid = !$isnonobjectid,"; WriteSource ".structmembers = $structmembers,"; WriteSource ".structmemberscount = $structmemberscount,"; WriteSource ".revgraphmembers = $revgraph,"; WriteSource ".revgraphmemberscount = $revgraphcount,"; WriteSource ".create = $create,"; WriteSource ".remove = $remove,"; WriteSource ".set = $set,"; WriteSource ".get = $get,"; WriteSource ".getstats = $getstats,"; WriteSource ".getstatsext = $getstatsext,"; WriteSource ".clearstats = $clearstats,"; WriteSource ".isexperimental = $isexperimental,"; WriteSource ".statenum = $statenum,"; WriteSource "};"; } WriteSectionComment "Object infos table"; WriteHeader "extern const sai_object_type_info_t* const sai_metadata_all_object_type_infos[];"; WriteSource "const sai_object_type_info_t* const sai_metadata_all_object_type_infos[] = {"; for my $ot (@objects) { if (not $ot =~ /^SAI_OBJECT_TYPE_(\w+)$/) { LogError "invalid object type '$ot'"; next; } if ($1 eq "NULL" or $1 eq "MAX") { WriteSource "NULL,"; next; } WriteSource "&sai_metadata_object_type_info_$ot,"; } WriteSource "NULL"; WriteSource "};"; } sub ExtractObjectsFromDesc { my ($struct, $member, $desc) = @_; $desc =~ s/@@/\n@@/g; while ($desc =~ /@@(\w+)(.+)/g) { my $tag = $1; my $val = $2; $val = Trim $val; next if not $tag eq "objects"; return ProcessTagObjects($struct, $member, $val); } return undef; } sub ProcessSingleNonObjectId { my $rawname = shift; my @types = @{ $SAI_ENUMS{sai_object_type_t}{values} }; my $structname = "sai_${rawname}_t"; LogDebug "ProcessSingleNonObjectId: processing $structname"; my $ot = "SAI_OBJECT_TYPE_" .uc(${rawname}); if (not grep(/$ot/,@types)) { LogError "struct $structname does not correspont to known object type"; return undef; } # NOTE: since this is a HASH then order of the members is not preserved as # they appear in struct definition my %struct = ExtractStructInfo($structname, "struct_"); for my $member (GetStructKeysInOrder(\%struct)) { my $type = $struct{$member}{type}; my $desc = $struct{$member}{desc}; # allowed entries on object structs if (not $type =~ /^sai_(nat_entry_data|mac|object_id|vlan_id|ip_address|ip_prefix|acl_chain|label_id|ip6|uint8|uint16|uint32|u32_range|\w+_type)_t$/) { LogError "struct member $member type '$type' is not allowed on struct $structname"; next; } next if not $type eq "sai_object_id_t"; my $objects = ExtractObjectsFromDesc($structname, $member, $desc); if (not defined $objects) { LogError "no object type defined on $structname $member"; next; } $struct{$member}{objects} = $objects; } return %struct; } sub ProcessNonObjectIdObjects { my @rawnames = GetNonObjectIdStructNames(); for my $rawname (@rawnames) { my %struct = ProcessSingleNonObjectId($rawname); my $objecttype = "SAI_OBJECT_TYPE_" . uc($rawname); $NON_OBJECT_ID_STRUCTS{$objecttype} = \%struct; } } sub GetHashOfAllAttributes { # list will be used to find attribute metadata # based on attribute string name my %ATTRIBUTES = (); for my $key (sort keys %SAI_ENUMS) { next if not $key =~ /^(sai_(\w+)_attr_t)$/; my $typedef = $1; my $enum = $SAI_ENUMS{$typedef}; my @values = @{ $enum->{values} }; for my $attr (@values) { if (not defined $METADATA{$typedef} or not defined $METADATA{$typedef}{$attr}) { LogError "metadata is missing for $attr"; next; } next if defined $METADATA{$typedef}{$attr}{ignore}; $ATTRIBUTES{$attr} = 1; } } return %ATTRIBUTES; } sub CheckCapabilities { my %ATTRIBUTES = GetHashOfAllAttributes(); for my $attr (keys %CAPABILITIES) { next if defined $ATTRIBUTES{$attr}; LogError "capability attribute $attr not found on all attributes list"; } } sub CreateListOfAllAttributes { # list will be used to find attribute metadata # based on attribute string name WriteSectionComment "List of all attributes"; my %ATTRIBUTES = GetHashOfAllAttributes(); WriteHeader "extern const sai_attr_metadata_t* const sai_metadata_attr_sorted_by_id_name[];"; WriteSource "const sai_attr_metadata_t* const sai_metadata_attr_sorted_by_id_name[] = {"; my @keys = sort keys %ATTRIBUTES; for my $attr (@keys) { WriteSource "&sai_metadata_attr_$attr,"; } my $count = @keys; WriteSource "NULL"; WriteSource "};"; WriteSource "const size_t sai_metadata_attr_sorted_by_id_name_count = $count;"; WriteHeader "extern const size_t sai_metadata_attr_sorted_by_id_name_count;"; } sub CheckApiStructNames { # # purpose of this check is to find out # whether sai_api_t enums match actual # struct of api declarations # my @values = @{ $SAI_ENUMS{"sai_api_t"}{values} }; for my $value (@values) { next if $value eq "SAI_API_UNSPECIFIED"; if (not $value =~ /^SAI_API_(\w+)$/) { LogError "invalie api name $value"; next; } my $api = lc $1; my $structName = "sai_${api}_api_t"; my $structFile = "struct_$structName.xml"; # doxygen doubles underscores $structFile =~ s/_/__/g; my $file = "$XMLDIR/$structFile"; if (not -e $file) { LogError "there is no struct $structName corresponding to api name $value"; } } for my $name (sort keys %ALL_STRUCTS) { next if not $name =~ /^sai_(\w+)_api_t$/; my $val = uc("SAI_API_$1"); if (not grep(/^$val$/,@values)) { LogError "struct '$name' defined, but enum entry $val is missing on sai_api_t"; } } } sub CheckApiDefines { # # purpose of this check is to check whether # all enum entries defined in sai_api_t # have corresponding structs defined for each # defined object like sai_fdb_api_t # my @apis = @{ $SAI_ENUMS{sai_api_t}{values} }; for my $api (@apis) { my $short = lc($1) if $api =~ /SAI_API_(\w+)/; next if $short eq "unspecified"; if (not defined $APITOOBJMAP{$short}) { LogError "$api is defined in sai.h but no corresponding struct for objects found"; } } } sub ExtractStatsFunctionMap { # # Purpose is to get statistics functions consistent # with stat_t defined for them # my @headers = GetHeaderFiles(); my @exheaders = GetExperimentalHeaderFiles(); my @merged = (@headers, @exheaders); my %otmap = (); for my $header (@merged) { my $data = ReadHeaderFile($header); next if not $data =~ m!(sai_\w+_api_t)(.+?)\1;!igs; my $apis = $2; my @fns = $apis =~ /sai_(\w+_stats(?:_ext)?)_fn/g; for my $fn (@fns) { # exceptions next if $fn eq "clear_port_all_stats"; next if $fn eq "get_tam_snapshot_stats"; if (not $fn =~ /^(?:get|clear)_(\w+)_stats(?:_ext)?$/) { LogWarning "Invalid stats function name: $fn"; } my $ot = $1; my @statfns = (); $otmap{$ot} = \@statfns if not defined $otmap{$ot}; my $ref = $otmap{$ot}; push@$ref,$fn; } } %OBJECT_TYPE_TO_STATS_MAP = %otmap; } sub ExtractObjectTypeBulkMap { # # Purpose is to get object types that have bulk API present # Note: not all objects have all 4 apis defined # my @headers = GetHeaderFiles(); my @exheaders = GetExperimentalHeaderFiles(); my @merged = (@headers, @exheaders); my %otmap = (); for my $header (@merged) { my $data = ReadHeaderFile($header); next if not $data =~ m!(sai_\w+_api_t)(.+?)\1;!igs; my $apis = $2; my @fns = $apis =~ /(sai_bulk_(?:\w+)_fn\s+(?:\w+))/g; for my $fn (@fns) { my $name; my $ot; if ($fn =~ /^sai_bulk_object_(create|remove|set|get)(?:_attribute)?_fn\s+(?:create|remove|set|get)_(\w+?)s(_attribute)?$/) { $name = $1; $ot = $2; } elsif ($fn =~ /^sai_bulk_(create|remove|set|get)_(\w+?)(?:_attribute)?_fn\s+(?:create|remove|set|get)_\w+?s(?:_attribute)?$/) { $name = $1; $ot = $2; } else { LogError "unrecognized bulk pattern: $fn"; next; } my $OT = "SAI_OBJECT_TYPE_" . uc($ot); if (not defined $OBJECT_TYPE_MAP{$OT}) { LogError "invalid object type $OT extracted from bulk definition: $fn"; next; } $otmap{$OT}{$name} = 1; } } %OBJECT_TYPE_BULK_MAP = %otmap; } sub CheckObjectTypeStatitics { # # Purpose is to check if each defined statistics for object type has 3 stat # functions defined and if there is corresponding object type for stat enum # for my $ot (sort keys %OBJECT_TYPE_TO_STATS_MAP) { my $ref = $OBJECT_TYPE_TO_STATS_MAP{$ot}; my $stats = "@$ref"; # each object type that supports statistics should have 3 stat functions (and in that order) my $expected = "get_${ot}_stats get_${ot}_stats_ext clear_${ot}_stats"; next if $stats eq $expected; LogWarning uc($ot) . " has only '$stats' functions, expected: $expected"; } for my $key (keys %SAI_ENUMS) { next if not $key =~ /sai_(\w+)_stat_t/; my $ot = $1; next if defined $OBJECT_TYPE_TO_STATS_MAP{$ot}; LogWarning "stats $key are defined, but no API 3 stat functions defined for $ot"; } } sub CheckAllEnumsEndings { my %all = (); for my $key (@ALL_ENUMS) { $all{$key} = 1; } # # Enums ending on START, END and RANGE_BASE are special and are designed to # specify range span of enumerations. Here we make sure that every START # enum has it's END enum, also each END enum should have ether START or # BASE enum defined. BASE enum don't may not specify END enum # for my $key (sort keys %all) { # exceptions next if $key eq "SAI_HOSTIF_TRAP_TYPE_CUSTOM_EXCEPTION_RANGE_BASE"; next if $key eq "SAI_IN_DROP_REASON_CUSTOM_RANGE_END"; next if $key eq "SAI_OUT_DROP_REASON_CUSTOM_RANGE_END"; if ($key =~ /^(\w+)_START$/) { LogWarning "expected END enum for $key" if not defined $all{"$1_END"}; } elsif ($key =~ /^(\w+_(CUSTOM|EXTENSIONS)_RANGE)_END$/) { LogWarning "expected START enum for $key" if not defined $all{"$1_START"}; } elsif ($key =~ /^(\w+_RANGE)_END$/) { LogWarning "expected BASE enum for $1 $key" if not defined $all{"$1_BASE"}; } elsif ($key =~ /^(\w+)_END$/) { LogWarning "expected START enum for $1 $key" if not defined $all{"$1_START"}; } elsif ($key =~ /^(\w+_CUSTOM_RANGE)_BASE$/) { LogInfo "no need for END enum for $key" if defined $all{"$1_END"}; } elsif ($key =~ /^(\w+_RANGE)_BASE$/) { LogWarning "expected END enum for $key" if not defined $all{"$1_END"}; } elsif ($key =~ /^(\w+)_BASE$/) { LogInfo "non range base $key"; } } } sub ExtractApiToObjectMap { # # Purpose is to get which object type # maps to which API, since multiple object types like acl # can map to one api structure # my @headers = GetHeaderFiles(); my @exheaders = GetExperimentalHeaderFiles(); my %exh = map { $_ => 1 } @exheaders; my @merged = (@headers, @exheaders); for my $header (@merged) { my $data = ReadHeaderFile($header); my @lines = split/\n/,$data; my $empty = 0; my $emptydoxy = 0; my @objects = (); my $api = undef; for my $line (@lines) { if ($line =~ /typedef\s+enum\s+_sai_(\w+)_attr_t/) { push@objects,uc("SAI_OBJECT_TYPE_$1"); } if ($line =~ /typedef\s+struct\s+_sai_(\w+)_api_t/) { $api = $1; last; } } if (not defined $api) { my $len = @objects; if ($len > 0) { LogError "api struct was not found in file $header, but objects are defined @objects"; next; } next; } my $shortapi = $api; $shortapi =~ s/_//g; my $correct = (defined $exh{$header}) ? "saiexperimental$shortapi.h" : "sai$shortapi.h"; if ($header ne $correct) { LogWarning "File $header should be named '$correct'"; } # NOTE: those maps will include experimental extensions for my $obj(@objects) { $OBJTOAPIMAP{$obj} = $api; $EXPERIMENTAL_OBJECTS{uc($obj)} = 1 if $correct =~ /^saiexperimental/; } $APITOOBJMAP{$api} = \@objects; } } sub GetReverseDependencyGraph { # # Purpose of this method is to generate reverse # dependency graph of where object ID are used # my %REVGRAPH = (); my @objects = @{ $SAI_ENUMS{sai_object_type_t}{values} }; for my $ot (@objects) { if (not $ot =~ /^SAI_OBJECT_TYPE_(\w+)$/) { LogError "invalid object type '$ot'"; next; } my $otname = $1; my $typedef = lc "sai_${otname}_attr_t"; next if $ot =~ /^SAI_OBJECT_TYPE_(MAX|NULL)$/; # for each objec types we need to scann all objects # also non object id structs my $enum = $SAI_ENUMS{$typedef}; my @values = @{ $enum->{values} }; for my $attr (@values) { # metadata of single attribute of this object type my $meta = $METADATA{$typedef}{$attr}; next if not defined $meta->{objects}; # we will also include RO attributes my @objects = @{ $meta->{objects} }; my $attrid = $meta->{attrid}; for my $usedot (@objects) { if (not defined $REVGRAPH{$usedot}) { my @arr = (); $REVGRAPH{$usedot} = \@arr; } my $ref = $REVGRAPH{$usedot}; push@$ref,"$ot,$attrid"; } } next if not defined $NON_OBJECT_ID_STRUCTS{$ot}; # handle non object id types my %struct = %{ $NON_OBJECT_ID_STRUCTS{$ot} }; for my $key (sort keys %struct) { next if not defined $struct{$key}{objects}; my @objs = @{ $struct{$key}{objects} }; for my $usedot (@objs) { if (not defined $REVGRAPH{$usedot}) { my @arr = (); $REVGRAPH{$usedot} = \@arr; } my $ref = $REVGRAPH{$usedot}; push@$ref,"$ot,$key"; } } } return %REVGRAPH; } sub WriteLoggerVariables { # # logger requires 2 variables # - log level # - log function # # we can extract this to another source file saimetadatalogger.c # but now seems to be unnecessary # WriteSectionComment "Loglevel variables"; WriteSource "volatile sai_log_level_t sai_metadata_log_level = SAI_LOG_LEVEL_NOTICE;"; WriteSource "volatile sai_metadata_log_fn sai_metadata_log = NULL;"; } my %ProcessedItems = (); sub ProcessStructItem { my ($type, $struct) = @_; $type = $1 if $struct =~ /^sai_(\w+)_list_t$/ and $type =~ /^(\w+)\*$/; return if defined $ProcessedItems{$type}; return if defined $SAI_ENUMS{$type}; # struct entry is enum return if $type eq "bool"; return if $type =~ /^sai_(u?int\d+|ip[46]|mac|cos|vlan_id|queue_index)_t/; # primitives, we could get that from defines return if $type =~ /^u?int\d+_t/; return if $type =~ /^sai_[su]\d+_list_t/; if ($type eq "sai_object_id_t" or $type eq "sai_object_list_t") { # NOTE: don't change that, we can't have object id's inside complicated structures LogError "type $type in $struct can't be used, please convert struct to new object type and this item to an attribute"; return; } my %S = (); if ($type =~ /^union (\w+)::(\w+)\s*$/) { # union is special, but now since all unions are named # then members are not flattened anyway, and we need to examine # entries from union xml # XXX may require revisit if union names will be complicated my $unionStructName = $1; my $unionName = $2; $unionStructName =~ s/_/__/g; $unionName =~ s/_/__/g; my $filename = "union${unionStructName}_1_1$unionName.xml"; %S = ExtractStructInfo($unionStructName, $filename); } else { %S = ExtractStructInfo($type, "struct_"); } for my $key (sort keys %S) { my $item = $S{$key}{type}; ProcessStructItem($item, $type); $ProcessedItems{$item} = 1; } my $count = scalar(keys %S); my @k = sort keys %S; if ($type =~ /^sai_(\w+)_list_t$/ and $count != 2) { LogError "lists must contain 2 elements (count, list), but $type has $count (@k), it's not a list then, fix this"; } } sub CheckAttributeValueUnion { # # purpose of this test is to find out if attribute # union contains complex structures members that also contain # object id, all object ids should be simple object id member oid # or object list objlist, other complext structures containing # objects are NOT supported since it will be VERY HARD to track # object dependencies via metadata and comparison logic # my %Union = ExtractStructInfo("sai_attribute_value_t", "union_"); my @primitives = qw/sai_acl_action_data_t sai_acl_field_data_t sai_pointer_t sai_object_id_t sai_object_list_t char/; for my $key (sort keys %Union) { my $type = $Union{$key}{type}; next if $type eq "char[32]"; next if $type =~ /sai_u?int\d+_t/; next if $type =~ /sai_[su]\d+_list_t/; next if defined $PRIMITIVE_TYPES{$type}; next if grep(/^$type$/, @primitives); ProcessStructItem($type, "sai_attribute_value_t"); } } sub CheckStatEnum { for my $key (keys %SAI_ENUMS) { next if not $key =~ /sai_(\w+)_stat_t/; my $ot = uc("SAI_OBJECT_TYPE_$1"); next if defined $OBJECT_TYPE_MAP{$ot}; LogError "stat enum defined $key but no object type $ot exists"; } } sub GetSwitchPointersInOrder { my $num = scalar @_; my @all = @{ $SAI_ENUMS{sai_switch_attr_t}{values} }; my @ordered = (); my $regex = "(" . join("|",@_) . ")"; for my $attr (@all) { next if not $METADATA{sai_switch_attr_t}{$attr}{type} =~ /$regex/; push@ordered,$1; } my $got = scalar @ordered; if ($got != $num) { LogError "missing attributes, expected $num, got $got"; } return @ordered; } sub CreateNotificationStruct { # # create notification struct for easier notification # manipulation in code # WriteSectionComment "SAI notifications struct"; WriteHeader "typedef struct _sai_switch_notifications_t {"; for my $name (GetSwitchPointersInOrder(keys %NOTIFICATIONS)) { if (not $name =~ /^sai_(\w+)_notification_fn/) { LogWarning "notification function $name is not ending on _notification_fn"; next; } WriteHeader "$name on_$1;"; } WriteHeader "} sai_switch_notifications_t;"; } sub CreateNotificationEnum { # # create notification enum for easier notification # manipulation in code # WriteSectionComment "SAI notifications enum"; my $typename = "sai_switch_notification_type_t"; WriteHeader "typedef enum _$typename {"; my $prefix = uc $typename; chop $prefix; my @values = (); for my $name (GetSwitchPointersInOrder(keys %NOTIFICATIONS)) { if (not $name =~ /^sai_(\w+)_notification_fn/) { LogWarning "notification function '$name' is not ending on _notification_fn"; next; } $name = uc $1; WriteHeader "${prefix}$name,"; push @values, "${prefix}$name"; } WriteHeader "} $typename;"; $SAI_ENUMS{$typename}{values} = \@values; WriteSectionComment "sai_switch_notification_type_t metadata"; ProcessSingleEnum($typename, $typename, $prefix); WriteSectionComment "Get sai_switch_notification_type_t helper method"; CreateEnumHelperMethod("sai_switch_notification_type_t"); } sub CreateNotificationNames { # # create notification names to have string representation # WriteSectionComment "SAI notifications names"; for my $name (GetSwitchPointersInOrder(keys %NOTIFICATIONS)) { if (not $name =~ /^sai_(\w+)_notification_fn/) { LogWarning "notification function '$name' is not ending on _notification_fn"; next; } $name = uc $1; WriteHeader "#define SAI_SWITCH_NOTIFICATION_NAME_$name \"$1\""; } } sub CreateSwitchNotificationAttributesList { # # create notification attributes list for easy use on places where only # notifications must be processed instead of looping through all switch # attributes # WriteSectionComment "SAI Switch Notification Attributes List"; WriteHeader "extern const sai_attr_metadata_t* const sai_metadata_switch_notify_attr[];"; WriteSource "const sai_attr_metadata_t* const sai_metadata_switch_notify_attr[] = {"; for my $name (GetSwitchPointersInOrder(keys %NOTIFICATIONS)) { next if not $name =~ /^sai_(\w+)_notification_fn/; WriteSource "&sai_metadata_attr_SAI_SWITCH_ATTR_" . uc($1) . "_NOTIFY,"; } WriteSource "NULL"; WriteSource "};"; my $count = scalar(keys %NOTIFICATIONS); WriteHeader "extern const size_t sai_metadata_switch_notify_attr_count;"; WriteSource "const size_t sai_metadata_switch_notify_attr_count = $count;"; WriteSectionComment "Define SAI_METADATA_SWITCH_NOTIFY_ATTR_COUNT"; WriteHeader "#define SAI_METADATA_SWITCH_NOTIFY_ATTR_COUNT $count"; } sub CreateSwitchNotificationsUpdateMethods { # # This function will generate methods for populate switch notifications # from attribute list, this will be handy when auto populating pointers # WriteSectionComment "SAI Update Switch Notification Pointers"; WriteHeader "sai_status_t sai_metadata_update_switch_notification_pointers("; WriteHeader " _Inout_ sai_switch_notifications_t *sn,"; WriteHeader " _In_ uint32_t count,"; WriteHeader " _In_ const sai_attribute_t* attrs);"; WriteSource "sai_status_t sai_metadata_update_switch_notification_pointers("; WriteSource " _Inout_ sai_switch_notifications_t *sn,"; WriteSource " _In_ uint32_t count,"; WriteSource " _In_ const sai_attribute_t* attrs)"; WriteSource "{"; WriteSource "if (sn == NULL || attrs == NULL)"; WriteSource "{"; WriteSource "SAI_META_LOG_ERROR(\"sn or attrs parameter is NULL\");"; WriteSource "return SAI_STATUS_INVALID_PARAMETER;"; WriteSource "}"; WriteSource ""; WriteSource "uint32_t idx = 0;"; WriteSource ""; WriteSource "for (; idx < count; idx++)"; WriteSource "{"; WriteSource "switch(attrs[idx].id)"; WriteSource "{"; for my $name (GetSwitchPointersInOrder(keys %NOTIFICATIONS)) { next if not $name =~ /^sai_(\w+)_notification_fn/; WriteSource "case SAI_SWITCH_ATTR_" . uc($1) . "_NOTIFY:"; WriteSource " sn->on_$1 = (sai_$1_notification_fn)attrs[idx].value.ptr;"; WriteSource " break;"; } WriteSource "default:"; WriteSource " break;"; WriteSource "}"; WriteSource "}"; WriteSource "return SAI_STATUS_SUCCESS;"; WriteSource "}"; WriteSectionComment "SAI Update Attribute Notification Pointers"; WriteHeader "sai_status_t sai_metadata_update_attribute_notification_pointers("; WriteHeader " _In_ const sai_switch_notifications_t *sn,"; WriteHeader " _In_ uint32_t count,"; WriteHeader " _Inout_ sai_attribute_t* attrs);"; WriteSource "sai_status_t sai_metadata_update_attribute_notification_pointers("; WriteSource " _In_ const sai_switch_notifications_t *sn,"; WriteSource " _In_ uint32_t count,"; WriteSource " _Inout_ sai_attribute_t* attrs)"; WriteSource "{"; WriteSource "if (sn == NULL || attrs == NULL)"; WriteSource "{"; WriteSource "SAI_META_LOG_ERROR(\"sn or attrs parameter is NULL\");"; WriteSource "return SAI_STATUS_INVALID_PARAMETER;"; WriteSource "}"; WriteSource ""; WriteSource "uint32_t idx = 0;"; WriteSource ""; WriteSource "for (; idx < count; idx++)"; WriteSource "{"; WriteSource "switch(attrs[idx].id)"; WriteSource "{"; for my $name (GetSwitchPointersInOrder(keys %NOTIFICATIONS)) { next if not $name =~ /^sai_(\w+)_notification_fn/; WriteSource "case SAI_SWITCH_ATTR_" . uc($1) . "_NOTIFY:"; WriteSource " attrs[idx].value.ptr = (void*)sn->on_$1;"; WriteSource " break;"; } WriteSource "default:"; WriteSource " break;"; WriteSource "}"; WriteSource "}"; WriteSource "return SAI_STATUS_SUCCESS;"; WriteSource "}"; } sub CreateSwitchPointersStruct { # # create pointersstruct for easier notification # manipulation in code # WriteSectionComment "SAI switch pointers struct"; WriteHeader "typedef struct _sai_switch_pointers_t {"; my @pointers = keys %NOTIFICATIONS; push @pointers, values %ATTR_TO_CALLBACK; for my $name (GetSwitchPointersInOrder(@pointers)) { if (not $name =~ /^sai_(\w+)_fn/) { LogWarning "pointer function $name is not ending on _fn"; next; } elsif ($name =~ /^sai_(\w+)_notification_fn/) { WriteHeader "$name on_$1;"; } elsif ($name =~ /^sai_(\w+)_fn/) { WriteHeader "$name on_$1;"; } } WriteHeader "} sai_switch_pointers_t;"; } sub CreateSwitchPointersEnum { # # create switch pointer enum for easie pointerr # manipulation in code # WriteSectionComment "SAI switch pointer enum"; my $typename = "sai_switch_pointer_type_t"; WriteHeader "typedef enum _$typename {"; my $prefix = uc $typename; chop $prefix; my @values = (); my @pointers = keys %NOTIFICATIONS; push @pointers, values %ATTR_TO_CALLBACK; for my $name (GetSwitchPointersInOrder(@pointers)) { if (not $name =~ /^sai_(\w+)_fn/) { LogWarning "function '$name' is not ending on _fn"; next; } elsif ($name =~ /^sai_(\w+)_notification_fn/) { $name = uc $1; WriteHeader "${prefix}$name,"; push @values, "${prefix}$name"; } elsif ($name =~ /^sai_(\w+)_fn/) { $name = uc $1; WriteHeader "${prefix}$name,"; push @values, "${prefix}$name"; } } WriteHeader "} $typename;"; $SAI_ENUMS{$typename}{values} = \@values; WriteSectionComment "sai_switch_pointer_type_t metadata"; ProcessSingleEnum($typename, $typename, $prefix); WriteSectionComment "Get sai_switch_pointer_type_t helper method"; CreateEnumHelperMethod("sai_switch_pointer_type_t"); } sub CreateSwitchPointersAttributesList { # # create switch pointers attributes list for easy use on places where only # pointers must be processed instead of looping through all switch # attributes # WriteSectionComment "SAI Switch Pointers Attributes List"; WriteHeader "extern const sai_attr_metadata_t* const sai_metadata_switch_pointers_attr[];"; WriteSource "const sai_attr_metadata_t* const sai_metadata_switch_pointers_attr[] = {"; my @pointers = keys %NOTIFICATIONS; push @pointers, values %ATTR_TO_CALLBACK; for my $name (sort @pointers) { next if not $name =~ /^sai_(\w+)_fn/; if ($name =~ /^sai_(\w+)_notification_fn/) { WriteSource "&sai_metadata_attr_SAI_SWITCH_ATTR_" . uc($1) . "_NOTIFY,"; } elsif ($name =~ /^sai_(?:switch_)(\w+)_fn/) { WriteSource "&sai_metadata_attr_SAI_SWITCH_ATTR_" . uc($1) . ","; } else { LogError("unmatched name '$name'"); } } WriteSource "NULL"; WriteSource "};"; my $count = scalar(@pointers); WriteHeader "extern const size_t sai_metadata_switch_pointers_attr_count;"; WriteSource "const size_t sai_metadata_switch_pointers_attr_count = $count;"; WriteSectionComment "Define SAI_METADATA_SWITCH_POINTERS_ATTR_COUNT"; WriteHeader "#define SAI_METADATA_SWITCH_POINTERS_ATTR_COUNT $count"; } sub WriteHeaderHeader { WriteSectionComment "AUTOGENERATED FILE! DO NOT EDIT"; WriteHeader "#ifndef __SAI_METADATA_H__"; WriteHeader "#define __SAI_METADATA_H__"; WriteHeader "#include <sai.h>"; WriteHeader "#include <saiextensions.h>"; WriteHeader "#include \"saimetadatatypes.h\""; WriteHeader "#include \"saimetadatautils.h\""; WriteHeader "#include \"saimetadatalogger.h\""; WriteHeader "#include \"saiserialize.h\""; } sub WriteHeaderFotter { WriteHeader "#endif /* __SAI_METADATA_H__ */"; } sub CreateSourcePragmaPop { WriteSourceSectionComment "Pragma diagnostic pop"; WriteSource "#pragma GCC diagnostic pop"; } sub ProcessXmlFiles { for my $file (GetSaiXmlFiles($XMLDIR)) { LogInfo "Processing $file"; ProcessXmlFile("$XMLDIR/$file"); } } sub ProcessValues { my ($refUnion, $refValueTypes, $refValueTypesToVt) = @_; for my $key (keys %$refUnion) { my $type = $refUnion->{$key}->{type}; next if $type eq "char[32]" or $type eq "bool"; if (not $type =~ /^sai_(\w+)_t$/) { LogWarning "skipping type $type, FIXME"; next; } my $innername = $1; $innername =~ s/^s(\d+)/INT$1/; $innername =~ s/^u(\d+)/UINT$1/; $innername =~ s/^ip(\d+)/IPV$1/; $refValueTypes->{$type} = $key; $refValueTypesToVt->{$type} = uc($innername); } } sub PopulateValueTypes { my %Union = ExtractStructInfo("sai_attribute_value_t", "union_"); ProcessValues(\%Union, \%VALUE_TYPES, \%VALUE_TYPES_TO_VT); %Union = ExtractStructInfo("sai_acl_action_parameter_t", "union_"); ProcessValues(\%Union, \%ACL_ACTION_TYPES, \%ACL_ACTION_TYPES_TO_VT); %Union = ExtractStructInfo("sai_acl_field_data_data_t", "union_"); ProcessValues(\%Union, \%ACL_FIELD_TYPES, \%ACL_FIELD_TYPES_TO_VT); } sub CreateObjectTypeMap { map { $OBJECT_TYPE_MAP{$_} = $_ } @{ $SAI_ENUMS{sai_object_type_t}{values} }; } sub ExtractUnionsInfo { my @files = GetXmlUnionFiles($XMLDIR); for my $file (@files) { my $ref = ReadXml $file; my $kind = $ref->{compounddef}[0]->{kind}; if ($kind ne "union") { LogError "expected '$file' to contain union but kind is '$kind'"; next; } my $def = $ref->{compounddef}[0]->{compoundname}[0]; if (not $def =~ /^(_sai_\w+::)*_(\w+)$/) { LogWarning "union name '$def' not match pattern: (_sai_\\w+::)*(_\\w+)"; next; } my $name = $2; LogError "Name $name should be in format sai_\\w+_t" if not $name =~ /^sai_\w+_t$/; $SAI_UNIONS{$name}{file} = $file; $SAI_UNIONS{$name}{name} = $name; $SAI_UNIONS{$name}{def} = $def; $SAI_UNIONS{$name}{nested} = 1 if $def =~ /::/; my %s = ExtractStructInfoEx($name, $file); # NOTE: validonly tag must exists on each member and is checked on serialize function # NOTE: extraparam tag must on struct description and is checked on serialize function } } sub LoadCapabilities { %CAPABILITIES = %{ GetCapabilities() }; } sub MergeExtensionsEnums { for my $exenum (sort keys%EXTENSIONS_ENUMS) { if (not $exenum =~ /^(sai_\w+)_extensions_t$/) { LogError "Enum $exenum is not extension enum"; next; } my $enum = "$1_t"; if (not defined $SAI_ENUMS{$enum}) { LogError "Enum $exenum is extending not existing enum $enum"; next; } my @exvalues = @{ $SAI_ENUMS{$exenum}{values} }; my @values = @{ $SAI_ENUMS{$enum}{values} }; push@values,@exvalues; $SAI_ENUMS{$enum}{values} = \@values; next if not $exenum =~ /_attr_extensions_t/; for my $exvalue (@exvalues) { $EXTENSIONS_ATTRS{$exvalue} = 1; $METADATA{$enum}{$exvalue} = $METADATA{$exenum}{$exvalue}; } } } sub ProcessNotificationStruct { my $rawname = shift; my @types = @{ $SAI_ENUMS{sai_object_type_t}{values} }; my $structname = "sai_${rawname}_t"; LogDebug "ProcessProcessNotificationStruct: processing $structname"; my %struct = ExtractStructInfo($structname, "struct_"); #print Dumper(%SAI_ENUMS); for my $member (GetStructKeysInOrder(\%struct)) { my $type = $struct{$member}{type}; my $desc = $struct{$member}{desc}; # allowed entries on notification object structs next if defined $SAI_ENUMS{$type}; # type is enum ! next if $type =~ /^sai_\w+_entry_t/; # non object id struct next if $type =~ /^(uint32_t|bool)$/; next if $type =~ /^(sai_twamp_session_stats_data_t)$/; if ($type =~ /^(sai_object_id_t|sai_attribute_t\*)$/) { my $objects = ExtractObjectsFromDesc($structname, $member, $desc); if (not defined $objects) { LogError "no object type defined on $structname $member"; next; } $struct{$member}{objects} = $objects; next; } LogWarning "$member $type"; } return %struct; } sub CreateOtherStructs { WriteSectionComment "Notifications structs members metadata"; my @ntfstructs = (); for my $name (sort keys %ALL_STRUCTS) { next if $name =~ /^sai_\w+_(api|list|entry)_t$/; next if not $name =~ /^sai_(\w+_notification(_data)?)_t$/; my $rawname = $1; my %struct = ProcessNotificationStruct($rawname); my$membersname = ProcessStructMembers(\%struct, "NULL", $rawname, 1); push@ntfstructs, $membersname; } for my $name(@ntfstructs) { WriteHeader "extern const sai_struct_member_info_t* const $name\[\];"; } } sub CreateSaiSwigGetApiHelperFunctions { # # write swig get api helper functions, those functions could be moved to # saimetadata.c directly, but inside sai_api_query is used, and currently # sai metadata can be compiled and linked without any SAI library # my @apis = @{ $SAI_ENUMS{sai_api_t}{values} }; WriteSwig "%{"; for my $Api (@apis) { $Api =~ /^SAI_API_(\w+)/; my $api = lc($1); next if $api =~ /unspecified/; WriteSwig "sai_status_t sai_get_${api}_api(sai_${api}_api_t* out)"; WriteSwig "{"; WriteSwig "sai_${api}_api_t* api;"; WriteSwig "sai_status_t status = sai_api_query((sai_api_t)$Api, (void**)&api);"; WriteSwig "if (status == SAI_STATUS_SUCCESS)"; WriteSwig "{"; WriteSwig "*out = *api;"; WriteSwig "}"; WriteSwig "return status;"; WriteSwig "}"; } WriteSwig "%}"; for my $Api (@apis) { $Api =~ /^SAI_API_(\w+)/; my $api = lc($1); next if $api =~ /unspecified/; WriteSwig "sai_status_t sai_get_${api}_api(sai_${api}_api_t* out);"; } } sub CreateSaiSwigApiStructs { # # for swig api to be callable, it needs to be created as a function in # structure, not as member # WriteSwig "%include \"saitypes.h\""; my @apis = @{ $SAI_ENUMS{sai_api_t}{values} }; for my $Api (@apis) { $Api =~ /^SAI_API_(\w+)/; my $api = lc($1); next if $api =~ /unspecified/; my $structname = "sai_${api}_api_t"; my %struct = ExtractStructInfo($structname, "struct_"); WriteSwig "typedef struct _$structname {"; for my $member (GetStructKeysInOrder(\%struct)) { my $type = $struct{$member}{type}; my $name = $struct{$member}{name}; if (not defined $FUNCTION_DEF{$type}) { LogError "function type $type is not defined for $api.$name"; next; } my $prototype = $FUNCTION_DEF{$type}; if (not $prototype =~ /^typedef (\S+)\(\* $type\) \((.+)\)$/) { LogError "failed to match function proto type $type is not defined for $api.$name"; next; } my $returntype = $1; my $params = $2; WriteSwig "$returntype $name($params);"; } WriteSwig "} $structname;"; WriteSwig ""; } for my $Api (@apis) { $Api =~ /^SAI_API_(\w+)/; my $api = lc($1); next if $api =~ /unspecified/; WriteSwig "%ignore sai_${api}_api_t;"; } my @headers = GetHeaderFiles(); my @metaheaders = GetMetaHeaderFiles(); my @exheaders = GetExperimentalHeaderFiles(); push(@metaheaders, "saimetadata.h"); my @merged = (@headers, @metaheaders, @exheaders); WriteSwig "%ignore sai_metadata_log;"; WriteSwig "%ignore sai_metadata_log_level;"; WriteSwig "%ignore sai_free_attribute;"; for my $header (sort @merged) { WriteSwig "%nodefaultctor;" if $header =~ /saimetadatatypes\.h/; WriteSwig "%include \"$header\""; WriteSwig "%clearnodefaultctor;" if $header =~ /saimetadatatypes\.h/; } } sub CreateDefineMaxConditionsLen { WriteSectionComment "Define SAI_METADATA_MAX_CONDITIONS_LEN"; WriteHeader "#define SAI_METADATA_MAX_CONDITIONS_LEN $MAX_CONDITIONS_LEN"; } # # MAIN # LoadCapabilities(); ExtractApiToObjectMap(); ExtractStatsFunctionMap(); ExtractUnionsInfo(); CheckHeadersStyle() if not defined $optionDisableStyleCheck; GetStructLists(); PopulateValueTypes(); ProcessXmlFiles(); MergeExtensionsEnums(); CreateObjectTypeMap(); ExtractObjectTypeBulkMap(); WriteHeaderHeader(); ProcessSaiStatus(); ProcessExtraRangeDefines(); CreateSourceIncludes(); CreateSourcePragmaPush(); CreateDeclareEveryEntryMacro(); CreateMetadataHeaderAndSource(); CreateMetadata(); CreateMetadataForAttributes(); CreateDefineMaxConditionsLen(); CreateEnumHelperMethods(); ProcessNonObjectIdObjects(); CreateOtherStructs(); CreateStructNonObjectId(); CreateApis(); CreateApisStruct(); CreateGlobalApis(); CreateGlobalFunctions(); CreateGenericQuadApi(); CreateGenericStatsApi(); CreateGenericQuadBulkApi(); CreateApisQuery(); CreateGlobalApisQuery(); CreateObjectInfo(); CreateListOfAllAttributes(); CheckCapabilities(); CheckApiStructNames(); CheckApiDefines(); CheckAttributeValueUnion(); CheckStatEnum(); CheckObjectTypeStatitics(); CheckAllEnumsEndings(); CreateNotificationStruct(); CreateNotificationEnum(); CreateNotificationNames(); CreateSwitchNotificationAttributesList(); CreateSwitchNotificationsUpdateMethods(); CreateSwitchPointersStruct(); CreateSwitchPointersEnum(); CreateSwitchPointersAttributesList(); CreateSerializeMethods(); CreateSaiSwigGetApiHelperFunctions(); CreateSaiSwigApiStructs(); WriteHeaderFotter(); CreateSourcePragmaPop(); # Test Section CreateTests(); WriteLoggerVariables(); WriteMetaDataFiles();