1#!/usr/bin/env perl 2 3# This script is used for generating callout lists from the MRW for devices 4# that can be accessed from the BMC. The callouts have a location code, the 5# target name (for debug), a priority, and in some cases a MRU. The script 6# supports I2C, FSI, SPI, FSI-I2C, and FSI-SPI devices. The output is a JSON 7# file organized into sections for each callout type, with keys based on the 8# type. I2c uses a bus and address, FSI uses a link, and SPI uses a bus 9# number. If FSI is combined with I2C or SPI, then the link plus the I2C/SPI 10# keys is used. Multi-hop FSI links are indicated by a dash in between the 11# links, eg "0-1". 12# 13# An example section is: 14# "FSI": 15# { 16# "5": 17# { 18# "Callouts":[ 19# { 20# "Priority":"H" 21# "LocationCode": "P1-C50", 22# "MRU":"/sys-0/node-0/motherboard/proc_socket-0/module-0/power9-0", 23# "Name":"/sys-0/node-0/motherboard/cpu0" 24# }, 25# { 26# "Priority":"H", 27# "LocationCode": "P1-C42", 28# "MRU":"/sys-0/node-0/motherboard/ebmc-card/BMC-0", 29# "Name":"/sys-0/node-0/motherboard/ebmc-card" 30# }, 31# { 32# "Priority":"L", 33# "LocationCode": "P1", 34# "Name":"/sys-0/node-0/motherboard" 35# } 36# ], 37# "Dest":"/sys-0/node-0/motherboard-0/proc_socket-0/module-0/power9-0", 38# "Source":"/sys-0/node-0/motherboard-0/ebmc-card-connector-0/card-0/bmc-0" 39# } 40# } 41# The Name, Dest and Source entries are MRW targets and are just for debug. 42# 43# The optional --segments argument will output a JSON file of all the bus 44# segments in the system, which is what the callouts are made from. 45 46use strict; 47use warnings; 48 49# Callout object 50# Base class for other callouts. 51# There is an object per device, so it can contain multiple 52# FRU callouts in the calloutList attribute. 53package Callout; 54sub new 55{ 56 my $class = shift; 57 my $self = { 58 type => shift, 59 sourceChip => shift, 60 destChip => shift, 61 calloutList => shift, 62 }; 63 64 return bless $self, $class; 65} 66 67sub sourceChip 68{ 69 my $self = shift; 70 return $self->{sourceChip}; 71} 72 73sub destChip 74{ 75 my $self = shift; 76 return $self->{destChip}; 77} 78 79sub type 80{ 81 my $self = shift; 82 return $self->{type}; 83} 84 85sub calloutList 86{ 87 my $self = shift; 88 return $self->{calloutList}; 89} 90 91# I2CCallout object for I2C callouts 92package I2CCallout; 93our @ISA = qw(Callout); 94sub new 95{ 96 my ($class) = @_; 97 # source, dest, calloutList 98 my $self = $class->SUPER::new("I2C", $_[1], $_[2], $_[3]); 99 $self->{i2cBus} = $_[4]; 100 $self->{i2cAddr} = $_[5]; 101 return bless $self, $class; 102} 103 104sub i2cBus 105{ 106 my $self = shift; 107 return $self->{i2cBus}; 108} 109 110sub i2cAddress 111{ 112 my $self = shift; 113 return $self->{i2cAddr}; 114} 115 116# FSICallout object for FSI callouts 117package FSICallout; 118our @ISA = qw(Callout); 119sub new 120{ 121 my ($class) = @_; 122 my $self = $class->SUPER::new("FSI", $_[1], $_[2], $_[3]); 123 $self->{FSILink} = $_[4]; 124 bless $self, $class; 125 return $self; 126} 127 128sub fsiLink 129{ 130 my $self = shift; 131 return $self->{FSILink}; 132} 133 134# SPICallout object for SPI callouts 135package SPICallout; 136our @ISA = qw(Callout); 137sub new 138{ 139 my ($class) = @_; 140 my $self = $class->SUPER::new("SPI", $_[1], $_[2], $_[3]); 141 $self->{SPIBus} = $_[4]; 142 bless $self, $class; 143 return $self; 144} 145 146sub spiBus 147{ 148 my $self = shift; 149 return $self->{SPIBus}; 150} 151 152# FSII2CCallout object for FSI-I2C callouts 153# Ideally this would be derived from FSICallout, but I can't 154# get it to work. 155package FSII2CCallout; 156 157our @ISA = qw(Callout); 158sub new 159{ 160 my ($class) = @_; 161 # source, dest, calloutList 162 my $self = $class->SUPER::new("FSI-I2C", $_[1], $_[2], $_[3]); 163 $self->{FSILink} = $_[4]; 164 $self->{i2cBus} = $_[5]; 165 $self->{i2cAddr} = $_[6]; 166 return bless $self, $class; 167} 168 169sub fsiLink 170{ 171 my $self = shift; 172 return $self->{FSILink}; 173} 174 175sub i2cBus 176{ 177 my $self = shift; 178 return $self->{i2cBus}; 179} 180 181sub i2cAddress 182{ 183 my $self = shift; 184 return $self->{i2cAddr}; 185} 186 187#FSISPICallout object for FSI-SPI callouts 188package FSISPICallout; 189 190our @ISA = qw(Callout); 191sub new 192{ 193 my ($class) = @_; 194 # source, dest, calloutList 195 my $self = $class->SUPER::new("FSI-SPI", $_[1], $_[2], $_[3]); 196 $self->{FSILink} = $_[4]; 197 $self->{SPIBus} = $_[5]; 198 return bless $self, $class; 199} 200 201sub fsiLink 202{ 203 my $self = shift; 204 return $self->{FSILink}; 205} 206 207sub spiBus 208{ 209 my $self = shift; 210 return $self->{SPIBus}; 211} 212 213package main; 214 215use mrw::Targets; 216use mrw::Util; 217use Getopt::Long; 218use File::Basename; 219use JSON; 220 221my $mrwFile = ""; 222my $outFile = ""; 223my $nonStrict = 0; 224my $printSegments = 0; 225 226# Not supporting priorities A, B, or C until necessary 227my %priorities = (H => 3, M => 2, L => 1); 228 229# Segment bus types 230my %busTypes = ( I2C => 1, FSIM => 1, FSICM => 1, SPI => 1 ); 231 232GetOptions( 233 "m=s" => \$mrwFile, 234 "o=s" => \$outFile, 235 "n" => \$nonStrict, 236 "segments" => \$printSegments 237) 238 or printUsage(); 239 240if (($mrwFile eq "") or ($outFile eq "")) 241{ 242 printUsage(); 243} 244 245# Load system MRW 246my $targets = Targets->new; 247$targets->loadXML($mrwFile); 248 249# Find all single segment buses that we care about 250my %allSegments = getPathSegments(); 251 252my @callouts; 253 254# Build the single and multi segment callouts 255buildCallouts(\%allSegments, \@callouts); 256 257printJSON(\@callouts); 258 259# Write the segments to a JSON file 260if ($printSegments) 261{ 262 my $outDir = dirname($outFile); 263 my $segmentsFile = "$outDir/segments.json"; 264 265 open(my $fh, '>', $segmentsFile) or 266 die "Could not open file '$segmentsFile' $!"; 267 268 my $json = JSON->new; 269 $json->indent(1); 270 $json->canonical(1); 271 my $text = $json->encode(\%allSegments); 272 print $fh $text; 273 close $fh; 274} 275 276# Returns a hash of all the FSI, I2C, and SPI segments in the MRW 277sub getPathSegments 278{ 279 my %segments; 280 foreach my $target (sort keys %{$targets->getAllTargets()}) 281 { 282 my $numConnections = $targets->getNumConnections($target); 283 284 if ($numConnections == 0) 285 { 286 next; 287 } 288 289 for (my $connIndex=0;$connIndex<$numConnections;$connIndex++) 290 { 291 my $connBusObj = $targets->getConnectionBus($target, $connIndex); 292 my $busType = $connBusObj->{bus_type}; 293 294 # We only care about certain bus types 295 if (not exists $busTypes{$busType}) 296 { 297 next; 298 } 299 300 my $dest = $targets->getConnectionDestination($target, $connIndex); 301 302 my %segment; 303 $segment{BusType} = $busType; 304 $segment{SourceUnit} = $target; 305 $segment{SourceChip} = getParentByClass($target, "CHIP"); 306 if ($segment{SourceChip} eq "") 307 { 308 if ($nonStrict) 309 { 310 print "Warning: Could not get parent chip for source $target\n"; 311 next; 312 } 313 else 314 { 315 die "Error: Could not get parent chip for source $target\n"; 316 } 317 } 318 319 $segment{DestUnit} = $dest; 320 $segment{DestChip} = getParentByClass($dest, "CHIP"); 321 322 # If the unit's direct parent is a connector that's OK too. 323 if ($segment{DestChip} eq "") 324 { 325 my $parent = $targets->getTargetParent($dest); 326 if ($targets->getAttribute($parent, "CLASS") eq "CONNECTOR") 327 { 328 $segment{DestChip} = $parent; 329 } 330 } 331 332 if ($segment{DestChip} eq "") 333 { 334 if ($nonStrict) 335 { 336 print "Warning: Could not get parent chip for dest $dest\n"; 337 next; 338 } 339 else 340 { 341 die "Error: Could not get parent chip for dest $dest\n"; 342 } 343 } 344 345 my $fruPath = $targets->getBusAttribute( 346 $target, $connIndex, "FRU_PATH"); 347 348 if (defined $fruPath) 349 { 350 $segment{FRUPath} = $fruPath; 351 my @callouts = getFRUPathCallouts($fruPath); 352 $segment{Callouts} = \@callouts; 353 } 354 else 355 { 356 $segment{FRUPath} = ""; 357 my @empty; 358 $segment{Callouts} = \@empty; 359 } 360 361 if ($busType eq "I2C") 362 { 363 # If the bus goes through a mux, then the I2C_BUS_ALIAS field 364 # will be filled in with the bus alias number. For example 365 # 28 could be an alias for bus 5 mux channel 2. Old parts may 366 # not have this attribute. Note this is the actual alias value 367 # so doesn't need a 1 subtracted later, so account for that now. 368 # 369 if (isValidBusAttribute($target, $connIndex, "I2C_BUS_ALIAS")) 370 { 371 $segment{I2CBus} = $targets->getBusAttribute( 372 $target, $connIndex, "I2C_BUS_ALIAS"); 373 374 if ($segment{I2CBus} ne "") 375 { 376 $segment{I2CBus} = $segment{I2CBus} + 1; 377 } 378 } 379 380 if ($segment{I2CBus} eq "") 381 { 382 $segment{I2CBus} = $targets->getAttribute($target, "I2C_PORT"); 383 } 384 385 $segment{I2CAddress} = 386 hex($targets->getAttribute($dest, "I2C_ADDRESS")); 387 388 # Convert to the 7 bit address that linux uses 389 $segment{I2CAddress} = 390 Util::adjustI2CAddress($segment{I2CAddress}); 391 } 392 elsif ($busType eq "FSIM") 393 { 394 $segment{FSILink} = 395 hex($targets->getAttribute($target, "FSI_LINK")); 396 } 397 elsif ($busType eq "SPI") 398 { 399 $segment{SPIBus} = $targets->getAttribute($target, "SPI_PORT"); 400 401 # Seems to be in HEX sometimes 402 if ($segment{SPIBus} =~ /^0x/i) 403 { 404 $segment{SPIBus} = hex($segment{SPIBus}); 405 } 406 } 407 408 push @{$segments{$busType}}, { %segment }; 409 } 410 } 411 412 return %segments; 413} 414 415#Breaks the FRU_PATH attribute up into its component callouts. 416#It looks like: "H:<some target>,L:<some other target>(<MRU>)" 417#Where H/L are the priorities and can be H/M/L. 418#The MRU that is in parentheses is optional and is a chip name on that 419#FRU target. 420sub getFRUPathCallouts 421{ 422 my @callouts; 423 my $fruPath = shift; 424 425 my @entries = split(',', $fruPath); 426 427 for my $entry (@entries) 428 { 429 my %callout; 430 my ($priority, $path) = split(':', $entry); 431 432 # pull the MRU out of the parentheses at the end and then 433 # remove the parentheses. 434 if ($path =~ /\(.+\)$/) 435 { 436 ($callout{MRU}) = $path =~ /\((.+)\)/; 437 438 $path =~ s/\(.+\)$//; 439 } 440 441 # check if the target we read out is valid by 442 # checking for a required attribute 443 if ($targets->isBadAttribute($path, "CLASS")) 444 { 445 die "FRU Path $path not a valid target\n"; 446 } 447 448 $callout{Priority} = $priority; 449 if (not exists $priorities{$priority}) 450 { 451 die "Invalid priority: '$priority' on callout $path\n"; 452 } 453 454 $callout{Name} = $path; 455 456 push @callouts, \%callout; 457 } 458 459 return @callouts; 460} 461 462# Returns an ancestor target based on its class 463sub getParentByClass 464{ 465 my ($target, $class) = @_; 466 my $parent = $targets->getTargetParent($target); 467 468 while (defined $parent) 469 { 470 if (!$targets->isBadAttribute($parent, "CLASS")) 471 { 472 if ($class eq $targets->getAttribute($parent, "CLASS")) 473 { 474 return $parent; 475 } 476 } 477 $parent = $targets->getTargetParent($parent); 478 } 479 480 return ""; 481} 482 483# Build the callout objects 484sub buildCallouts 485{ 486 my ($segments, $callouts) = @_; 487 488 # Callouts for 1 segment connections directly off of the BMC. 489 buildBMCSingleSegmentCallouts($segments, $callouts); 490 491 # Callouts more than 1 segment away 492 buildMultiSegmentCallouts($segments, $callouts); 493} 494 495# Build the callout objects for devices 1 segment away. 496sub buildBMCSingleSegmentCallouts 497{ 498 my ($segments, $callouts) = @_; 499 500 for my $busType (keys %$segments) 501 { 502 for my $segment (@{$$segments{$busType}}) 503 { 504 my $chipType = $targets->getType($segment->{SourceChip}); 505 if ($chipType eq "BMC") 506 { 507 my $callout = buildSingleSegmentCallout($segment); 508 509 if (defined $callout) 510 { 511 push @{$callouts}, $callout; 512 } 513 } 514 } 515 } 516} 517 518# Build the callout object based on the callout type using the 519# callout list from the single segment. 520sub buildSingleSegmentCallout 521{ 522 my ($segment, $callouts) = @_; 523 524 if ($segment->{BusType} eq "I2C") 525 { 526 return createI2CCallout($segment, $callouts); 527 } 528 elsif ($segment->{BusType} eq "FSIM") 529 { 530 return createFSICallout($segment, $callouts); 531 } 532 elsif ($segment->{BusType} eq "SPI") 533 { 534 return createSPICallout($segment, $callouts); 535 } 536 537 return undef; 538} 539 540# Build the callout objects for devices more than 1 segment away from 541# the BMC. All but the last segment will always be FSI. The FRU 542# callouts accumulate as segments are added. 543sub buildMultiSegmentCallouts 544{ 545 my ($segments, $callouts) = @_; 546 my $hops = 0; 547 my $found = 1; 548 549 # Connect FSI link callouts to other FSI segments to make new callouts, and 550 # connect all FSI link callouts up with the I2C/SPI segments to make even 551 # more new callouts. Note: Deal with I2C muxes, if they are ever modeled, 552 # when there are some. 553 554 # Each time through the loop, go out another FSI hop. 555 # Stop when no more new hops are found. 556 while ($found) 557 { 558 my @newCallouts; 559 $found = 0; 560 561 for my $callout (@$callouts) 562 { 563 if ($callout->type() ne "FSI") 564 { 565 next; 566 } 567 568 # link numbers are separated by '-'s in the link field, 569 # so 0-5 = 1 hop 570 my @numHops = $callout->fsiLink() =~ /(-)/g; 571 572 # only deal with callout objects that contain $hops hops. 573 if ($hops != scalar @numHops) 574 { 575 next; 576 } 577 578 for my $segmentType (keys %$segments) 579 { 580 for my $segment (@{$$segments{$segmentType}}) 581 { 582 # If the destination on this callout is the same 583 # as the source of the segment, then make a new 584 # callout that spans both. 585 if ($callout->destChip() eq $segment->{SourceChip}) 586 { 587 # First build the new single segment callout 588 my $segmentCallout = 589 buildSingleSegmentCallout($segment); 590 591 # Now merge both callouts into one. 592 if (defined $segmentCallout) 593 { 594 my $newCallout = 595 mergeCallouts($callout, $segmentCallout); 596 597 push @newCallouts, $newCallout; 598 $found = 1; 599 } 600 } 601 } 602 } 603 } 604 605 if ($found) 606 { 607 push @{$callouts}, @newCallouts; 608 } 609 610 $hops = $hops + 1; 611 } 612} 613 614# Merge 2 callout objects into 1 that contains all of their FRU callouts. 615sub mergeCallouts 616{ 617 my ($firstCallout, $secondCallout) = @_; 618 619 # This callout list will be merged/sorted later. 620 # Endpoint callouts are added first, so they will be higher 621 # in the callout list (priority permitting). 622 my @calloutList; 623 push @calloutList, @{$secondCallout->calloutList()}; 624 push @calloutList, @{$firstCallout->calloutList()}; 625 626 # FSI 627 if (($firstCallout->type() eq "FSI") && ($secondCallout->type() eq "FSI")) 628 { 629 # combine the FSI links with a - 630 my $FSILink = $firstCallout->fsiLink() . "-" . 631 $secondCallout->fsiLink(); 632 633 my $fsiCallout = new FSICallout($firstCallout->sourceChip(), 634 $secondCallout->destChip(), \@calloutList, $FSILink); 635 636 return $fsiCallout; 637 } 638 # FSI-I2C 639 elsif (($firstCallout->type() eq "FSI") && 640 ($secondCallout->type() eq "I2C")) 641 { 642 my $i2cCallout = new FSII2CCallout($firstCallout->sourceChip(), 643 $secondCallout->destChip(), \@calloutList, 644 $firstCallout->fsiLink(), $secondCallout->i2cBus(), 645 $secondCallout->i2cAddress()); 646 647 return $i2cCallout; 648 } 649 # FSI-SPI 650 elsif (($firstCallout->type() eq "FSI") && 651 ($secondCallout->type() eq "SPI")) 652 { 653 my $spiCallout = new FSISPICallout($firstCallout->sourceChip(), 654 $secondCallout->destChip(), \@calloutList, 655 $firstCallout->fsiLink(), $secondCallout->spiBus()); 656 657 return $spiCallout; 658 } 659 660 die "Unrecognized callouts to merge: " . $firstCallout->type() . 661 " " . $secondCallout->type() . "\n"; 662} 663 664# Create an I2CCallout object 665sub createI2CCallout 666{ 667 my $segment = shift; 668 my $bus = $segment->{I2CBus}; 669 670 # Convert MRW BMC I2C numbering to the linux one for the BMC 671 if ($targets->getAttribute($segment->{SourceChip}, "TYPE") eq "BMC") 672 { 673 $bus = Util::adjustI2CPort($segment->{I2CBus}); 674 675 if ($bus < 0) 676 { 677 die "After adjusting BMC I2C bus $segment->{I2CBus}, " . 678 "got a negative number\n"; 679 } 680 } 681 682 my $i2cCallout = new I2CCallout($segment->{SourceChip}, 683 $segment->{DestChip}, $segment->{Callouts}, $bus, 684 $segment->{I2CAddress}); 685 686 return $i2cCallout; 687} 688 689# Create an FSICallout object 690sub createFSICallout 691{ 692 my $segment = shift; 693 694 my $fsiCallout = new FSICallout($segment->{SourceChip}, 695 $segment->{DestChip}, $segment->{Callouts}, 696 $segment->{FSILink}, $segment); 697 698 return $fsiCallout; 699} 700 701# Create a SPICallout object 702sub createSPICallout 703{ 704 my $segment = shift; 705 706 my $spiCallout = new SPICallout($segment->{SourceChip}, 707 $segment->{DestChip}, $segment->{Callouts}, 708 $segment->{SPIBus}); 709 710 return $spiCallout; 711} 712 713# Convert all of the *Callout objects to JSON and write them to a file. 714# It will convert the callout target names to location codes and also sort 715# the callout list before doing so. 716sub printJSON 717{ 718 my $callouts = shift; 719 my %output; 720 721 for my $callout (@$callouts) 722 { 723 my %c; 724 $c{Source} = $callout->sourceChip(); 725 $c{Dest} = $callout->destChip(); 726 727 for my $fruCallout (@{$callout->calloutList()}) 728 { 729 my %entry; 730 731 if (length($fruCallout->{Name}) == 0) 732 { 733 die "Could not get target name for a callout on path:\n" . 734 " (" . $callout->sourceChip() . ") ->\n" . 735 " (" . $callout->destChip() . ")\n"; 736 } 737 738 $entry{Name} = $fruCallout->{Name}; 739 740 $entry{LocationCode} = 741 Util::getLocationCode($targets, $fruCallout->{Name}); 742 743 # MRUs - for now just use the path + MRU name 744 if (exists $fruCallout->{MRU}) 745 { 746 $entry{MRU} = $entry{Name} . "/" . $fruCallout->{MRU}; 747 } 748 749 $entry{Priority} = validatePriority($fruCallout->{Priority}); 750 751 push @{$c{Callouts}}, \%entry; 752 } 753 754 755 # Remove duplicate callouts and sort 756 @{$c{Callouts}} = sortCallouts(@{$c{Callouts}}); 757 758 # Setup the entry based on the callout type 759 if ($callout->type() eq "I2C") 760 { 761 # The address key is in decimal, but save the hex value 762 # for easier debug. 763 $c{HexAddress} = $callout->i2cAddress(); 764 my $decimal = hex($callout->i2cAddress()); 765 766 $output{"I2C"}{$callout->i2cBus()}{$decimal} = \%c; 767 } 768 elsif ($callout->type() eq "SPI") 769 { 770 $output{"SPI"}{$callout->spiBus()} = \%c; 771 } 772 elsif ($callout->type() eq "FSI") 773 { 774 $output{"FSI"}{$callout->fsiLink()} = \%c; 775 } 776 elsif ($callout->type() eq "FSI-I2C") 777 { 778 $c{HexAddress} = $callout->i2cAddress(); 779 my $decimal = hex($callout->i2cAddress()); 780 781 $output{"FSI-I2C"}{$callout->fsiLink()} 782 {$callout->i2cBus()}{$decimal} = \%c; 783 } 784 elsif ($callout->type() eq "FSI-SPI") 785 { 786 $output{"FSI-SPI"}{$callout->fsiLink()}{$callout->spiBus()} = \%c; 787 } 788 } 789 790 open(my $fh, '>', $outFile) or die "Could not open file '$outFile' $!"; 791 my $json = JSON->new->utf8; 792 $json->indent(1); 793 $json->canonical(1); 794 my $text = $json->encode(\%output); 795 print $fh $text; 796 close $fh; 797} 798 799# This will remove duplicate callouts from the input callout list, keeping 800# the highest priority value and MRU, and then also sort by priority. 801# 802# There could be duplicates when multiple single segment callouts are 803# combined into 1. 804sub sortCallouts 805{ 806 my @callouts = @_; 807 808 # This will undef the duplicates, and then remove them at the end, 809 for (my $i = 0; $i < (scalar @callouts) - 1; $i++) 810 { 811 next if not defined $callouts[$i]; 812 813 for (my $j = $i + 1; $j < (scalar @callouts); $j++) 814 { 815 next if not defined $callouts[$j]; 816 817 if ($callouts[$i]->{LocationCode} eq $callouts[$j]->{LocationCode}) 818 { 819 # Keep the highest priority value 820 $callouts[$i]->{Priority} = getHighestPriority( 821 $callouts[$i]->{Priority}, $callouts[$j]->{Priority}); 822 823 # Keep the MRU if present 824 if (defined $callouts[$j]->{MRU}) 825 { 826 $callouts[$i]->{MRU} = $callouts[$j]->{MRU}; 827 } 828 829 $callouts[$j] = undef; 830 } 831 } 832 } 833 834 # removed the undefined ones 835 @callouts = grep {defined ($_)} @callouts; 836 837 # sort from highest to lowest priorities 838 @callouts = sort { 839 $priorities{$b->{Priority}} <=> $priorities{$a->{Priority}} 840 } @callouts; 841 842 return @callouts; 843} 844 845# Returns the highest priority value of the two passed in 846sub getHighestPriority 847{ 848 my ($p1, $p2) = @_; 849 850 if ($priorities{$p1} > $priorities{$p2}) 851 { 852 return $p1; 853 } 854 return $p2; 855} 856 857# Dies if the input priority isn't valid 858sub validatePriority 859{ 860 my $priority = shift; 861 862 if (not exists $priorities{$priority}) 863 { 864 die "Invalid callout priority found: $priority\n"; 865 } 866 867 return $priority; 868} 869 870# Check if the attribute is present on the bus 871sub isValidBusAttribute 872{ 873 my $target = shift; 874 my $connIndex = shift; 875 my $attr = shift; 876 877 if (defined($targets->getTarget($target)-> 878 {CONNECTION}->{BUS}->[$connIndex]->{bus_attribute}-> 879 {$attr}->{default})) 880 { 881 return 1; 882 } 883 return 0; 884} 885 886sub printUsage 887{ 888 print "$0 -m <MRW file> -o <Output filename> [--segments] [-n]\n" . 889 " -m <MRW file> = The MRW XML\n" . 890 " -o <Output filename> = The output JSON\n" . 891 " -n = Non-strict - Don't fail on some MRW structure problems\n" . 892 " [--segments] = Optionally create a segments.json file\n"; 893 exit(1); 894} 895