|  | #!/usr/bin/env perl | 
|  |  | 
|  | # Copyright 2016 by Frank Rowand | 
|  | # Copyright 2016 by Gaurav Minocha | 
|  | # | 
|  | # This file is subject to the terms and conditions of the GNU General Public | 
|  | # License v2. | 
|  |  | 
|  | use strict 'refs'; | 
|  | use strict subs; | 
|  |  | 
|  | use Getopt::Long; | 
|  |  | 
|  | $VUFX = "160610a"; | 
|  |  | 
|  | $script_name = $0; | 
|  | $script_name =~ s|^.*/||; | 
|  |  | 
|  |  | 
|  | # ----- constants for print_flags() | 
|  |  | 
|  | # Position in string $pr_flags.  Range of 0..($num_pr_flags - 1). | 
|  | $pr_flag_pos_mcompatible       = 0; | 
|  | $pr_flag_pos_driver            = 1; | 
|  | $pr_flag_pos_mdriver           = 2; | 
|  | $pr_flag_pos_config            = 3; | 
|  | $pr_flag_pos_mconfig           = 4; | 
|  | $pr_flag_pos_node_not_enabled  = 5; | 
|  | $pr_flag_pos_white_list        = 6; | 
|  | $pr_flag_pos_hard_coded        = 7; | 
|  | $pr_flag_pos_config_hard_coded = 8; | 
|  | $pr_flag_pos_config_none       = 9; | 
|  | $pr_flag_pos_config_m          = 10; | 
|  | $pr_flag_pos_config_y          = 11; | 
|  | $pr_flag_pos_config_test_fail  = 12; | 
|  |  | 
|  | $num_pr_flags = $pr_flag_pos_config_test_fail + 1; | 
|  |  | 
|  | # flags in @pr_flag_value must be unique values to allow simple regular | 
|  | # expessions to work for --include_flags and --exclude_flags. | 
|  | # Convention: use upper case letters for potential issues or problems. | 
|  |  | 
|  | @pr_flag_value = ('M', 'd', 'D', 'c', 'C', 'E', 'W', 'H', 'x', 'n', 'm', 'y', 'F'); | 
|  |  | 
|  | @pr_flag_help = ( | 
|  | "multiple compatibles found for this node", | 
|  | "driver found for this compatible", | 
|  | "multiple drivers found for this compatible", | 
|  | "kernel config found for this driver", | 
|  | "multiple config options found for this driver", | 
|  | "node is not enabled", | 
|  | "compatible is white listed", | 
|  | "matching driver and/or kernel config is hard coded", | 
|  | "kernel config hard coded in Makefile", | 
|  | "one or more kernel config file options is not set", | 
|  | "one or more kernel config file options is set to 'm'", | 
|  | "one or more kernel config file options is set to 'y'", | 
|  | "one of more kernel config file options fails to have correct value" | 
|  | ); | 
|  |  | 
|  |  | 
|  | # ----- | 
|  |  | 
|  | %driver_config = ();   # driver config array, indexed by driver source file | 
|  | %driver_count = ();    # driver_cnt, indexed by compatible | 
|  | %compat_driver = ();   # compatible driver array, indexed by compatible | 
|  | %existing_config = (); # existing config symbols present in given config file | 
|  | # expected values are: "y", "m", a decimal number, a | 
|  | # hex number, or a string | 
|  |  | 
|  | # ----- magic compatibles, do not have a driver | 
|  | # | 
|  | # Will not search for drivers for these compatibles. | 
|  |  | 
|  | %compat_white_list = ( | 
|  | 'none'                  => '1', | 
|  | 'pci'                   => '1', | 
|  | 'simple-bus'            => '1', | 
|  | ); | 
|  |  | 
|  | # Will not search for drivers for these compatibles. | 
|  | # | 
|  | # These compatibles have a very large number of false positives. | 
|  | # | 
|  | # 'hardcoded_no_driver' is a magic value.  Other code knows this | 
|  | # magic value.  Do not use 'no_driver' here! | 
|  | # | 
|  | # Revisit each 'hardcoded_no_driver' to see how the compatible | 
|  | # is used.  Are there drivers that can be provided? | 
|  |  | 
|  | %driver_hard_code_list = ( | 
|  | 'cache'                 => ['hardcoded_no_driver'], | 
|  | 'eeprom'                => ['hardcoded_no_driver'], | 
|  | 'gpio'                  => ['hardcoded_no_driver'], | 
|  | 'gpio-keys'             => ['drivers/input/keyboard/gpio_keys.c'], | 
|  | 'i2c-gpio'              => ['drivers/i2c/busses/i2c-gpio.c'], | 
|  | 'isa'                   => ['arch/mips/mti-malta/malta-dt.c', | 
|  | 'arch/x86/kernel/devicetree.c'], | 
|  | 'led'                   => ['hardcoded_no_driver'], | 
|  | 'm25p32'                => ['hardcoded_no_driver'], | 
|  | 'm25p64'                => ['hardcoded_no_driver'], | 
|  | 'm25p80'                => ['hardcoded_no_driver'], | 
|  | 'mtd-ram'               => ['drivers/mtd/maps/physmap_of.c'], | 
|  | 'pwm-backlight'         => ['drivers/video/backlight/pwm_bl.c'], | 
|  | 'spidev'                => ['hardcoded_no_driver'], | 
|  | 'syscon'                => ['drivers/mfd/syscon.c'], | 
|  | 'tlv320aic23'           => ['hardcoded_no_driver'], | 
|  | 'wm8731'                => ['hardcoded_no_driver'], | 
|  | ); | 
|  |  | 
|  | # Use these config options instead of searching makefiles | 
|  |  | 
|  | %driver_config_hard_code_list = ( | 
|  |  | 
|  | # this one needed even if %driver_hard_code_list is empty | 
|  | 'no_driver'                             => ['no_config'], | 
|  | 'hardcoded_no_driver'                   => ['no_config'], | 
|  |  | 
|  | # drivers/usb/host/ehci-ppc-of.c | 
|  | # drivers/usb/host/ehci-xilinx-of.c | 
|  | #  are included from: | 
|  | #    drivers/usb/host/ehci-hcd.c | 
|  | #  thus the search of Makefile for the included .c files is incorrect | 
|  | # ehci-hcd.c wraps the includes with ifdef CONFIG_USB_EHCI_HCD_..._OF | 
|  | # | 
|  | # similar model for ohci-hcd.c (but no ohci-xilinx-of.c) | 
|  | # | 
|  | # similarly, uhci-hcd.c includes uhci-platform.c | 
|  |  | 
|  | 'drivers/usb/host/ehci-ppc-of.c'        => ['CONFIG_USB_EHCI_HCD', | 
|  | 'CONFIG_USB_EHCI_HCD_PPC_OF'], | 
|  | 'drivers/usb/host/ohci-ppc-of.c'        => ['CONFIG_USB_OHCI_HCD', | 
|  | 'CONFIG_USB_OHCI_HCD_PPC_OF'], | 
|  |  | 
|  | 'drivers/usb/host/ehci-xilinx-of.c'     => ['CONFIG_USB_EHCI_HCD', | 
|  | 'CONFIG_USB_EHCI_HCD_XILINX'], | 
|  |  | 
|  | 'drivers/usb/host/uhci-platform.c'      => ['CONFIG_USB_UHCI_HCD', | 
|  | 'CONFIG_USB_UHCI_PLATFORM'], | 
|  |  | 
|  | # scan_makefile will find only one of these config options: | 
|  | #    ifneq ($(CONFIG_SOC_IMX6)$(CONFIG_SOC_LS1021A),) | 
|  | 'arch/arm/mach-imx/platsmp.c'           => ['CONFIG_SOC_IMX6 && CONFIG_SMP', | 
|  | 'CONFIG_SOC_LS1021A && CONFIG_SMP'], | 
|  | ); | 
|  |  | 
|  |  | 
|  | # 'virt/kvm/arm/.*' are controlled by makefiles in other directories, | 
|  | # using relative paths, such as 'KVM := ../../../virt/kvm'.  Do not | 
|  | # add complexity to find_kconfig() to deal with this.  There is a long | 
|  | # term intent to change the kvm related makefiles to the normal kernel | 
|  | # style.  After that is done, this entry can be removed from the | 
|  | # black_list_driver. | 
|  |  | 
|  | @black_list_driver = ( | 
|  | # kvm no longer a problem after commit 503a62862e8f in 4.7-rc1 | 
|  | # 'virt/kvm/arm/.*', | 
|  | ); | 
|  |  | 
|  |  | 
|  | sub usage() | 
|  | { | 
|  | print | 
|  | " | 
|  | Usage: $script_name [options] device-tree... | 
|  |  | 
|  | device_tree is: dts_file | dtb_file | proc_device-tree | 
|  |  | 
|  |  | 
|  | Valid options: | 
|  | -c FILE             Read kernel config options from FILE | 
|  | --config FILE        synonym for 'c' | 
|  | --config-format      config file friendly output format | 
|  | --exclude-flag FLAG  exclude entries with a matching flag | 
|  | -h                  Display this message and exit | 
|  | --help               synonym for 'h' | 
|  | --black-list-driver  use driver black list | 
|  | --white-list-config  use config white list | 
|  | --white-list-driver  use driver white list | 
|  | --include-flag FLAG  include only entries with a matching flag | 
|  | --include-suspect    include only entries with an uppercase flag | 
|  | --short-name         do not show the path portion of the node name | 
|  | --show-lists         report of white and black lists | 
|  | --version            Display program version and exit | 
|  |  | 
|  |  | 
|  | Report driver source files that match the compatibles in the device | 
|  | tree file and the kernel config options that enable the driver source | 
|  | files. | 
|  |  | 
|  | This program must be run in the root directory of a Linux kernel | 
|  | source tree. | 
|  |  | 
|  | The default format is a report that is intended to be easily human | 
|  | scannable. | 
|  |  | 
|  | An alternate format can be selected by --config-format.  This will | 
|  | create output that can easily be edited to create a fragment that can | 
|  | be appended to the existing kernel config file.  Each entry consists of | 
|  | multiple lines.  The first line reports flags, the node path, compatible | 
|  | value, driver file matching the compatible, configuration options, and | 
|  | current values of the configuration options.  For each configuration | 
|  | option, the following lines report the current value and the value that | 
|  | is required for the driver file to be included in the kernel. | 
|  |  | 
|  | If a large number of drivers or config options is listed for a node, | 
|  | and the '$pr_flag_value[$pr_flag_pos_hard_coded]' flag is set consider using --white-list-config and/or | 
|  | --white-list-driver.  If the white list option suppresses the correct | 
|  | entry please report that as a bug. | 
|  |  | 
|  | CAUTION: | 
|  | This program uses heuristics to guess which driver(s) support each | 
|  | compatible string and which config option(s) enables the driver(s). | 
|  | Do not believe that the reported information is fully correct. | 
|  | This program is intended to aid the process of determining the | 
|  | proper kernel configuration for a device tree, but this is not | 
|  | a fully automated process -- human involvement may still be | 
|  | required! | 
|  |  | 
|  | The driver match heuristic used is to search for source files | 
|  | containing the compatible string enclosed in quotes. | 
|  |  | 
|  | This program might not be able to find all drivers matching a | 
|  | compatible string. | 
|  |  | 
|  | Some makefiles are overly clever.  This program was not made | 
|  | complex enough to handle them.  If no config option is listed | 
|  | for a driver, look at the makefile for the driver source file. | 
|  | Even if a config option is listed for a driver, some other | 
|  | available config options may not be listed. | 
|  |  | 
|  | FLAG values: | 
|  | "; | 
|  |  | 
|  | for ($k = 0; $k < $num_pr_flags; $k++) { | 
|  | printf "     %s   %s\n", $pr_flag_value[$k], $pr_flag_help[$k]; | 
|  | } | 
|  |  | 
|  | print | 
|  | " | 
|  | Upper case letters indicate potential issues or problems. | 
|  |  | 
|  | The flag: | 
|  |  | 
|  | "; | 
|  |  | 
|  | $k = $pr_flag_pos_hard_coded; | 
|  | printf "     %s   %s\n", $pr_flag_value[$k], $pr_flag_help[$k]; | 
|  |  | 
|  | print | 
|  | " | 
|  | will be set if the config or driver is in the white lists, even if | 
|  | --white-list-config and --white-list-driver are not specified. | 
|  | This is a hint that 1) many of these reported lines are likely to | 
|  | be incorrect, and 2) using those options will reduce the number of | 
|  | drivers and/or config options reported. | 
|  |  | 
|  | --white-list-config and --white-list-driver may not be accurate if this | 
|  | program is not well maintained.  Use them with appropriate skepticism. | 
|  | Use the --show-lists option to report the values in the list. | 
|  |  | 
|  | Return value: | 
|  | 0   if no error | 
|  | 1   error processing command line | 
|  | 2   unable to open or read kernel config file | 
|  | 3   unable to open or process input device tree file(s) | 
|  |  | 
|  | EXAMPLES: | 
|  |  | 
|  | dt_to_config arch/arm/boot/dts/my_dts_file.dts | 
|  |  | 
|  | Basic report. | 
|  |  | 
|  | dt_to_config \\ | 
|  | --config \${KBUILD_OUTPUT}/.config \\ | 
|  | arch/\${ARCH}/boot/dts/my_dts_file.dts | 
|  |  | 
|  | Full report, with config file issues noted. | 
|  |  | 
|  | dt_to_config --include-suspect \\ | 
|  | --config \${KBUILD_OUTPUT}/.config \\ | 
|  | arch/\${ARCH}/boot/dts/my_dts_file.dts | 
|  |  | 
|  | Report of node / compatible string / driver tuples that should | 
|  | be further investigated.  A node may have multiple compatible | 
|  | strings.  A compatible string may be matched by multiple drivers. | 
|  | A driver may have config file issues noted.  The compatible string | 
|  | and/or driver may be in the white lists. | 
|  |  | 
|  | dt_to_config --include-suspect --config-format \\ | 
|  | --config ${KBUILD_OUTPUT}/.config \\ | 
|  | arch/\${ARCH}/boot/dts/my_dts_file.dts | 
|  |  | 
|  | Report of node / compatible string / driver tuples that should | 
|  | be further investigated.  The report can be edited to uncomment | 
|  | the config options to select the desired tuple for a given node. | 
|  | A node may have multiple compatible strings.  A compatible string | 
|  | may be matched by multiple drivers.  A driver may have config file | 
|  | issues noted.  The compatible string and/or driver may be in the | 
|  | white lists. | 
|  |  | 
|  | "; | 
|  | } | 
|  |  | 
|  | sub set_flag() | 
|  | { | 
|  | # pr_flags_ref is a reference to $pr_flags | 
|  |  | 
|  | my $pr_flags_ref = shift; | 
|  | my $pos          = shift; | 
|  |  | 
|  | substr $$pr_flags_ref, $pos, 1, $pr_flag_value[$pos]; | 
|  |  | 
|  | return $pr_flags; | 
|  | } | 
|  |  | 
|  | sub print_flags() | 
|  | { | 
|  | # return 1 if anything printed, else 0 | 
|  |  | 
|  | # some fields of pn_arg_ref might not be used in this function, but | 
|  | # extract all of them anyway. | 
|  | my $pn_arg_ref     = shift; | 
|  |  | 
|  | my $compat         = $pn_arg_ref->{compat}; | 
|  | my $compatible_cnt = $pn_arg_ref->{compatible_cnt}; | 
|  | my $config         = $pn_arg_ref->{config}; | 
|  | my $config_cnt     = $pn_arg_ref->{config_cnt}; | 
|  | my $driver         = $pn_arg_ref->{driver}; | 
|  | my $driver_cnt     = $pn_arg_ref->{driver_cnt}; | 
|  | my $full_node      = $pn_arg_ref->{full_node}; | 
|  | my $node           = $pn_arg_ref->{node}; | 
|  | my $node_enabled   = $pn_arg_ref->{node_enabled}; | 
|  | my $white_list     = $pn_arg_ref->{white_list}; | 
|  |  | 
|  | my $pr_flags       = '-' x $num_pr_flags; | 
|  |  | 
|  |  | 
|  | # ----- set flags in $pr_flags | 
|  |  | 
|  | if ($compatible_cnt > 1) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_mcompatible); | 
|  | } | 
|  |  | 
|  | if ($config_cnt > 1) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_mconfig); | 
|  | } | 
|  |  | 
|  | if ($driver_cnt >= 1) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_driver); | 
|  | } | 
|  |  | 
|  | if ($driver_cnt > 1) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_mdriver); | 
|  | } | 
|  |  | 
|  | # These strings are the same way the linux kernel tests. | 
|  | # The ePapr lists of values is slightly different. | 
|  | if (!( | 
|  | ($node_enabled eq "") || | 
|  | ($node_enabled eq "ok") || | 
|  | ($node_enabled eq "okay") | 
|  | )) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_node_not_enabled); | 
|  | } | 
|  |  | 
|  | if ($white_list) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_white_list); | 
|  | } | 
|  |  | 
|  | if (exists($driver_hard_code_list{$compat}) || | 
|  | (exists($driver_config_hard_code_list{$driver}) && | 
|  | ($driver ne "no_driver"))) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_hard_coded); | 
|  | } | 
|  |  | 
|  | my @configs = split(' && ', $config); | 
|  | for $configs (@configs) { | 
|  | $not = $configs =~ /^!/; | 
|  | $configs =~ s/^!//; | 
|  |  | 
|  | if (($configs ne "no_config") && ($configs ne "no_makefile")) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_config); | 
|  | } | 
|  |  | 
|  | if (($config_cnt >= 1) && | 
|  | ($configs !~ /CONFIG_/) && | 
|  | (($configs ne "no_config") && ($configs ne "no_makefile"))) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_config_hard_coded); | 
|  | } | 
|  |  | 
|  | my $existing_config = $existing_config{$configs}; | 
|  | if ($existing_config eq "m") { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_config_m); | 
|  | # Possible fail, depends on whether built in or | 
|  | # module is desired. | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_config_test_fail); | 
|  | } elsif ($existing_config eq "y") { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_config_y); | 
|  | if ($not) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_config_test_fail); | 
|  | } | 
|  | } elsif (($config_file) && ($configs =~ /CONFIG_/)) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_config_none); | 
|  | if (!$not) { | 
|  | &set_flag(\$pr_flags, $pr_flag_pos_config_test_fail); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | # ----- include / exclude filters | 
|  |  | 
|  | if ($include_flag_pattern && ($pr_flags !~ m/$include_flag_pattern/)) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if ($exclude_flag_pattern && ($pr_flags =~ m/$exclude_flag_pattern/)) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if ($config_format) { | 
|  | print "# "; | 
|  | } | 
|  | print "$pr_flags : "; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | sub print_node() | 
|  | { | 
|  | # return number of lines printed | 
|  |  | 
|  | # some fields of pn_arg_ref might not be used in this function, but | 
|  | # extract all of them anyway. | 
|  | my $pn_arg_ref     = shift; | 
|  |  | 
|  | my $compat         = $pn_arg_ref->{compat}; | 
|  | my $compatible_cnt = $pn_arg_ref->{compatible_cnt}; | 
|  | my $config         = $pn_arg_ref->{config}; | 
|  | my $config_cnt     = $pn_arg_ref->{config_cnt}; | 
|  | my $driver         = $pn_arg_ref->{driver}; | 
|  | my $driver_cnt     = $pn_arg_ref->{driver_cnt}; | 
|  | my $full_node      = $pn_arg_ref->{full_node}; | 
|  | my $node           = $pn_arg_ref->{node}; | 
|  | my $node_enabled   = $pn_arg_ref->{node_enabled}; | 
|  | my $white_list     = $pn_arg_ref->{white_list}; | 
|  |  | 
|  | my $separator; | 
|  |  | 
|  | if (! &print_flags($pn_arg_ref)) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | if ($short_name) { | 
|  | print "$node"; | 
|  | } else { | 
|  | print "$full_node"; | 
|  | } | 
|  | print " : $compat : $driver : $config : "; | 
|  |  | 
|  | my @configs = split(' && ', $config); | 
|  |  | 
|  | if ($config_file) { | 
|  | for $configs (@configs) { | 
|  | $configs =~ s/^!//; | 
|  | my $existing_config = $existing_config{$configs}; | 
|  | if (!$existing_config) { | 
|  | # check for /-m/, /-y/, or /-objs/ | 
|  | if ($configs !~ /CONFIG_/) { | 
|  | $existing_config = "x"; | 
|  | }; | 
|  | }; | 
|  | if ($existing_config) { | 
|  | print "$separator", "$existing_config"; | 
|  | $separator = ", "; | 
|  | } else { | 
|  | print "$separator", "n"; | 
|  | $separator = ", "; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | print "none"; | 
|  | } | 
|  |  | 
|  | print "\n"; | 
|  |  | 
|  | if ($config_format) { | 
|  | for $configs (@configs) { | 
|  | $not = $configs =~ /^!/; | 
|  | $configs =~ s/^!//; | 
|  | my $existing_config = $existing_config{$configs}; | 
|  |  | 
|  | if ($not) { | 
|  | if ($configs !~ /CONFIG_/) { | 
|  | print "# $configs\n"; | 
|  | } elsif ($existing_config eq "m") { | 
|  | print "# $configs is m\n"; | 
|  | print "# $configs=n\n"; | 
|  | } elsif ($existing_config eq "y") { | 
|  | print "# $configs is set\n"; | 
|  | print "# $configs=n\n"; | 
|  | } else { | 
|  | print "# $configs is not set\n"; | 
|  | print "# $configs=n\n"; | 
|  | } | 
|  |  | 
|  | } else { | 
|  | if ($configs !~ /CONFIG_/) { | 
|  | print "# $configs\n"; | 
|  | } elsif ($existing_config eq "m") { | 
|  | print "# $configs is m\n"; | 
|  | print "# $configs=y\n"; | 
|  | } elsif ($existing_config eq "y") { | 
|  | print "# $configs is set\n"; | 
|  | print "# $configs=y\n"; | 
|  | } else { | 
|  | print "# $configs is not set\n"; | 
|  | print "# $configs=y\n"; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | sub scan_makefile | 
|  | { | 
|  | my $pn_arg_ref    = shift; | 
|  | my $driver        = shift; | 
|  |  | 
|  | # ----- Find Kconfig symbols that enable driver | 
|  |  | 
|  | my ($dir, $base) = $driver =~ m{(.*)/(.*).c}; | 
|  |  | 
|  | my $makefile = $dir . "/Makefile"; | 
|  | if (! -r $makefile) { | 
|  | $makefile = $dir . "/Kbuild"; | 
|  | } | 
|  | if (! -r $makefile) { | 
|  | my $config; | 
|  |  | 
|  | $config = 'no_makefile'; | 
|  | push @{ $driver_config{$driver} }, $config; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!open(MAKEFILE_FILE, "<", "$makefile")) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | my $line; | 
|  | my @config; | 
|  | my @if_config; | 
|  | my @make_var; | 
|  |  | 
|  | NEXT_LINE: | 
|  | while ($next_line = <MAKEFILE_FILE>) { | 
|  | my $config; | 
|  | my $if_config; | 
|  | my $ifdef; | 
|  | my $ifeq; | 
|  | my $ifndef; | 
|  | my $ifneq; | 
|  | my $ifdef_config; | 
|  | my $ifeq_config; | 
|  | my $ifndef_config; | 
|  | my $ifneq_config; | 
|  |  | 
|  | chomp($next_line); | 
|  | $line = $line . $next_line; | 
|  | if ($next_line =~ /\\$/) { | 
|  | $line =~ s/\\$/ /; | 
|  | next NEXT_LINE; | 
|  | } | 
|  | if ($line =~ /^\s*#/) { | 
|  | $line = ""; | 
|  | next NEXT_LINE; | 
|  | } | 
|  |  | 
|  | # -----  condition ... else ... endif | 
|  |  | 
|  | if ($line =~ /^([ ]\s*|)else\b/) { | 
|  | $if_config = "!" . pop @if_config; | 
|  | $if_config =~ s/^!!//; | 
|  | push @if_config, $if_config; | 
|  | $line =~ s/^([ ]\s*|)else\b//; | 
|  | } | 
|  |  | 
|  | ($null, $ifeq_config,  $ifeq_config_val )  = $line =~ /^([ ]\s*|)ifeq\b.*\b(CONFIG_[A-Za-z0-9_]*)(.*)/; | 
|  | ($null, $ifneq_config, $ifneq_config_val)  = $line =~ /^([ ]\s*|)ifneq\b.*\b(CONFIG_[A-Za-z0-9_]*)(.*)/; | 
|  | ($null, $ifdef_config)                     = $line =~ /^([ ]\s*|)ifdef\b.*\b(CONFIG_[A-Za-z0-9_]*)/; | 
|  | ($null, $ifndef_config)                    = $line =~ /^([ ]\s*|)ifndef\b.*\b(CONFIG_[A-Za-z0-9_]*)/; | 
|  |  | 
|  | ($null, $ifeq)   = $line =~ /^([ ]\s*|)ifeq\b\s*(.*)/; | 
|  | ($null, $ifneq)  = $line =~ /^([ ]\s*|)ifneq\b\s*(.*)/; | 
|  | ($null, $ifdef)  = $line =~ /^([ ]\s*|)ifdef\b\s*(.*)/; | 
|  | ($null, $ifndef) = $line =~ /^([ ]\s*|)ifndef\b\s*(.*)/; | 
|  |  | 
|  | # Order of tests is important.  Prefer "CONFIG_*" regex match over | 
|  | # less specific regex match. | 
|  | if ($ifdef_config) { | 
|  | $if_config = $ifdef_config; | 
|  | } elsif ($ifeq_config) { | 
|  | if ($ifeq_config_val =~ /y/) { | 
|  | $if_config = $ifeq_config; | 
|  | } else { | 
|  | $if_config = "!" . $ifeq_config; | 
|  | } | 
|  | } elsif ($ifndef_config) { | 
|  | $if_config = "!" . $ifndef_config; | 
|  | } elsif ($ifneq_config) { | 
|  | if ($ifneq_config_val =~ /y/) { | 
|  | $if_config = "!" . $ifneq_config; | 
|  | } else { | 
|  | $if_config = $ifneq_config; | 
|  | } | 
|  | } elsif ($ifdef) { | 
|  | $if_config = $ifdef; | 
|  | } elsif ($ifeq) { | 
|  | $if_config = $ifeq; | 
|  | } elsif ($ifndef) { | 
|  | $if_config = "!" . $ifndef; | 
|  | } elsif ($ifneq) { | 
|  | $if_config = "!" . $ifneq; | 
|  | } else { | 
|  | $if_config = ""; | 
|  | } | 
|  | $if_config =~ s/^!!//; | 
|  |  | 
|  | if ($if_config) { | 
|  | push @if_config, $if_config; | 
|  | $line = ""; | 
|  | next NEXT_LINE; | 
|  | } | 
|  |  | 
|  | if ($line =~ /^([ ]\s*|)endif\b/) { | 
|  | pop @if_config; | 
|  | $line = ""; | 
|  | next NEXT_LINE; | 
|  | } | 
|  |  | 
|  | # ----- simple CONFIG_* = *.[co]  or  xxx [+:?]*= *.[co] | 
|  | # Most makefiles select on *.o, but | 
|  | # arch/powerpc/boot/Makefile selects on *.c | 
|  |  | 
|  | ($config) = $line =~ /(CONFIG_[A-Za-z0-9_]+).*\b$base.[co]\b/; | 
|  |  | 
|  | # ----- match a make variable instead of *.[co] | 
|  | # Recursively expanded variables are not handled. | 
|  |  | 
|  | if (!$config) { | 
|  | my $make_var; | 
|  | ($make_var) = $line =~ /\s*(\S+?)\s*[+:\?]*=.*\b$base.[co]\b/; | 
|  | if ($make_var) { | 
|  | if ($make_var =~ /[a-zA-Z0-9]+-[ym]/) { | 
|  | $config = $make_var; | 
|  | } elsif ($make_var =~ /[a-zA-Z0-9]+-objs/) { | 
|  | $config = $make_var; | 
|  | } else { | 
|  | push @make_var, $make_var; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!$config) { | 
|  | for $make_var (@make_var) { | 
|  | ($config) = $line =~ /(CONFIG_[A-Za-z0-9_]+).*\b$make_var\b/; | 
|  | last if ($config); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!$config) { | 
|  | for $make_var (@make_var) { | 
|  | ($config) = $line =~ /\s*(\S+?)\s*[+:\?]*=.*\b$make_var\b/; | 
|  | last if ($config); | 
|  | } | 
|  | } | 
|  |  | 
|  | # ----- next if no config found | 
|  |  | 
|  | if (!$config) { | 
|  | $line = ""; | 
|  | next NEXT_LINE; | 
|  | } | 
|  |  | 
|  | for $if_config (@if_config) { | 
|  | $config = $if_config . " && " . $config; | 
|  | } | 
|  |  | 
|  | push @{ $driver_config{$driver} }, $config; | 
|  |  | 
|  | $line = ""; | 
|  | } | 
|  |  | 
|  | close(MAKEFILE_FILE); | 
|  |  | 
|  | } | 
|  |  | 
|  |  | 
|  | sub find_kconfig | 
|  | { | 
|  | my $pn_arg_ref    = shift; | 
|  | my $driver        = shift; | 
|  |  | 
|  | my $lines_printed = 0; | 
|  | my @configs; | 
|  |  | 
|  | if (!@{ $driver_config{$driver} }) { | 
|  | &scan_makefile($pn_arg_ref, $driver); | 
|  | if (!@{ $driver_config{$driver} }) { | 
|  | push @{ $driver_config{$driver} }, "no_config"; | 
|  | } | 
|  | } | 
|  |  | 
|  | @configs = @{ $driver_config{$driver} }; | 
|  |  | 
|  | $$pn_arg_ref{config_cnt} = $#configs + 1; | 
|  | for my $config (@configs) { | 
|  | $$pn_arg_ref{config} = $config; | 
|  | $lines_printed += &print_node($pn_arg_ref); | 
|  | } | 
|  |  | 
|  | return $lines_printed; | 
|  | } | 
|  |  | 
|  |  | 
|  | sub handle_compatible() | 
|  | { | 
|  | my $full_node     = shift; | 
|  | my $node          = shift; | 
|  | my $compatible    = shift; | 
|  | my $node_enabled  = shift; | 
|  |  | 
|  | my $compat; | 
|  | my $lines_printed = 0; | 
|  | my %pn_arg        = (); | 
|  |  | 
|  | return if (!$node or !$compatible); | 
|  |  | 
|  | # Do not process compatible property of root node, | 
|  | # it is used to match board, not to bind a driver. | 
|  | return if ($node eq "/"); | 
|  |  | 
|  | $pn_arg{full_node}    = $full_node; | 
|  | $pn_arg{node}         = $node; | 
|  | $pn_arg{node_enabled} = $node_enabled; | 
|  |  | 
|  | my @compatibles = split('", "', $compatible); | 
|  |  | 
|  | $compatibles[0] =~ s/^"//; | 
|  | $compatibles[$#compatibles] =~ s/"$//; | 
|  |  | 
|  | $pn_arg{compatible_cnt} = $#compatibles + 1; | 
|  |  | 
|  | COMPAT: | 
|  | for $compat (@compatibles) { | 
|  |  | 
|  | $pn_arg{compat}     = $compat; | 
|  | $pn_arg{driver_cnt} = 0; | 
|  | $pn_arg{white_list} = 0; | 
|  |  | 
|  | if (exists($compat_white_list{$compat})) { | 
|  | $pn_arg{white_list} = 1; | 
|  | $pn_arg{driver}     = "no_driver"; | 
|  | $pn_arg{config_cnt} = 1; | 
|  | $pn_arg{config}     = "no_config"; | 
|  | $lines_printed += &print_node(\%pn_arg); | 
|  | next COMPAT; | 
|  | } | 
|  |  | 
|  | # ----- if compat previously seen, use cached info | 
|  |  | 
|  | if (exists($compat_driver{$compat})) { | 
|  | for my $driver (@{ $compat_driver{$compat} }) { | 
|  | $pn_arg{driver}     = $driver; | 
|  | $pn_arg{driver_cnt} = $driver_count{$compat}; | 
|  | $pn_arg{config_cnt} = $#{ $driver_config{$driver}} + 1; | 
|  |  | 
|  | for my $config (@{ $driver_config{$driver} }) { | 
|  | $pn_arg{config} = $config; | 
|  | $lines_printed += &print_node(\%pn_arg); | 
|  | } | 
|  |  | 
|  | if (!@{ $driver_config{$driver} }) { | 
|  | # no config cached yet | 
|  | # $driver in %driver_hard_code_list | 
|  | # but not %driver_config_hard_code_list | 
|  | $lines_printed += &find_kconfig(\%pn_arg, $driver); | 
|  | } | 
|  | } | 
|  | next COMPAT; | 
|  | } | 
|  |  | 
|  |  | 
|  | # ----- Find drivers (source files that contain compatible) | 
|  |  | 
|  | # this will miss arch/sparc/include/asm/parport.h | 
|  | # It is better to move the compatible out of the .h | 
|  | # than to add *.h. to the files list, because *.h generates | 
|  | # a lot of false negatives. | 
|  | my $files = '"*.c"'; | 
|  | my $drivers = `git grep -l '"$compat"' -- $files`; | 
|  | chomp($drivers); | 
|  | if ($drivers eq "") { | 
|  | $pn_arg{driver} = "no_driver"; | 
|  | $pn_arg{config_cnt} = 1; | 
|  | $pn_arg{config} = "no_config"; | 
|  | push @{ $compat_driver{$compat} }, "no_driver"; | 
|  | $lines_printed += &print_node(\%pn_arg); | 
|  | next COMPAT; | 
|  | } | 
|  |  | 
|  | my @drivers = split("\n", $drivers); | 
|  | $driver_count{$compat} = $#drivers + 1; | 
|  | $pn_arg{driver_cnt}    = $#drivers + 1; | 
|  |  | 
|  | DRIVER: | 
|  | for my $driver (@drivers) { | 
|  | push @{ $compat_driver{$compat} }, $driver; | 
|  | $pn_arg{driver} = $driver; | 
|  |  | 
|  | # ----- if driver previously seen, use cached info | 
|  |  | 
|  | $pn_arg{config_cnt} = $#{ $driver_config{$driver} } + 1; | 
|  | for my $config (@{ $driver_config{$driver} }) { | 
|  | $pn_arg{config} = $config; | 
|  | $lines_printed += &print_node(\%pn_arg); | 
|  | } | 
|  | if (@{ $driver_config{$driver} }) { | 
|  | next DRIVER; | 
|  | } | 
|  |  | 
|  | if ($black_list_driver) { | 
|  | for $black (@black_list_driver) { | 
|  | next DRIVER if ($driver =~ /^$black$/); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | # ----- Find Kconfig symbols that enable driver | 
|  |  | 
|  | $lines_printed += &find_kconfig(\%pn_arg, $driver); | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | # White space (line) between nodes for readability. | 
|  | # Each node may report several compatibles. | 
|  | # For each compatible, multiple drivers may be reported. | 
|  | # For each driver, multiple CONFIG_ options may be reported. | 
|  | if ($lines_printed) { | 
|  | print "\n"; | 
|  | } | 
|  | } | 
|  |  | 
|  | sub read_dts() | 
|  | { | 
|  | my $file         = shift; | 
|  |  | 
|  | my $compatible   = ""; | 
|  | my $line; | 
|  | my $node         = ""; | 
|  | my $node_enabled = ""; | 
|  |  | 
|  | if (! -r $file) { | 
|  | print STDERR "file '$file' is not readable or does not exist\n"; | 
|  | exit 3; | 
|  | } | 
|  |  | 
|  | if (!open(DT_FILE, "-|", "$dtx_diff $file")) { | 
|  | print STDERR "\n"; | 
|  | print STDERR "shell command failed:\n"; | 
|  | print STDERR "   $dtx_diff $file\n"; | 
|  | print STDERR "\n"; | 
|  | exit 3; | 
|  | } | 
|  |  | 
|  | FILE: | 
|  | while ($line = <DT_FILE>) { | 
|  | chomp($line); | 
|  |  | 
|  | if ($line =~ /{/) { | 
|  |  | 
|  | &handle_compatible($full_node, $node, $compatible, | 
|  | $node_enabled); | 
|  |  | 
|  | while ($end_node_count-- > 0) { | 
|  | pop @full_node; | 
|  | }; | 
|  | $end_node_count = 0; | 
|  | $full_node = @full_node[-1]; | 
|  |  | 
|  | $node = $line; | 
|  | $node =~ s/^\s*(.*)\s+\{.*/$1/; | 
|  | $node =~ s/.*: //; | 
|  | if ($node eq '/' ) { | 
|  | $full_node = '/'; | 
|  | } elsif ($full_node ne '/') { | 
|  | $full_node = $full_node . '/' . $node; | 
|  | } else { | 
|  | $full_node = '/' . $node; | 
|  | } | 
|  | push @full_node, $full_node; | 
|  |  | 
|  | $compatible = ""; | 
|  | $node_enabled = ""; | 
|  | next FILE; | 
|  | } | 
|  |  | 
|  | if ($line =~ /}/) { | 
|  | $end_node_count++; | 
|  | } | 
|  |  | 
|  | if ($line =~ /(\s+|^)status =/) { | 
|  | $node_enabled = $line; | 
|  | $node_enabled =~ s/^\t*//; | 
|  | $node_enabled =~ s/^status = "//; | 
|  | $node_enabled =~ s/";$//; | 
|  | next FILE; | 
|  | } | 
|  |  | 
|  | if ($line =~ /(\s+|^)compatible =/) { | 
|  | # Extract all compatible entries for this device | 
|  | # White space matching here and in handle_compatible() is | 
|  | # precise, because input format is the output of dtc, | 
|  | # which is invoked by dtx_diff. | 
|  | $compatible = $line; | 
|  | $compatible =~ s/^\t*//; | 
|  | $compatible =~ s/^compatible = //; | 
|  | $compatible =~ s/;$//; | 
|  | } | 
|  | } | 
|  |  | 
|  | &handle_compatible($full_node, $node, $compatible, $node_enabled); | 
|  |  | 
|  | close(DT_FILE); | 
|  | } | 
|  |  | 
|  |  | 
|  | sub read_config_file() | 
|  | { | 
|  | if (! -r $config_file) { | 
|  | print STDERR "file '$config_file' is not readable or does not exist\n"; | 
|  | exit 2; | 
|  | } | 
|  |  | 
|  | if (!open(CONFIG_FILE, "<", "$config_file")) { | 
|  | print STDERR "open $config_file failed\n"; | 
|  | exit 2; | 
|  | } | 
|  |  | 
|  | my @line; | 
|  |  | 
|  | LINE: | 
|  | while ($line = <CONFIG_FILE>) { | 
|  | chomp($line); | 
|  | next LINE if ($line =~ /^\s*#/); | 
|  | next LINE if ($line =~ /^\s*$/); | 
|  | @line = split /=/, $line; | 
|  | $existing_config{@line[0]} = @line[1]; | 
|  | } | 
|  |  | 
|  | close(CONFIG_FILE); | 
|  | } | 
|  |  | 
|  |  | 
|  | sub cmd_line_err() | 
|  | { | 
|  | my $msg = shift; | 
|  |  | 
|  | print STDERR "\n"; | 
|  | print STDERR "   ERROR processing command line options\n"; | 
|  | print STDERR "         $msg\n" if ($msg ne ""); | 
|  | print STDERR "\n"; | 
|  | print STDERR "   For help, type '$script_name --help'\n"; | 
|  | print STDERR "\n"; | 
|  | } | 
|  |  | 
|  |  | 
|  | # ----------------------------------------------------------------------------- | 
|  | # program entry point | 
|  |  | 
|  | Getopt::Long::Configure("no_ignore_case", "bundling"); | 
|  |  | 
|  | if (!GetOptions( | 
|  | "c=s"               => \$config_file, | 
|  | "config=s"          => \$config_file, | 
|  | "config-format"     => \$config_format, | 
|  | "exclude-flag=s"    => \@exclude_flag, | 
|  | "h"                 => \$help, | 
|  | "help"              => \$help, | 
|  | "black-list-driver" => \$black_list_driver, | 
|  | "white-list-config" => \$white_list_config, | 
|  | "white-list-driver" => \$white_list_driver, | 
|  | "include-flag=s"    => \@include_flag, | 
|  | "include-suspect"   => \$include_suspect, | 
|  | "short-name"        => \$short_name, | 
|  | "show-lists"        => \$show_lists, | 
|  | "version"           => \$version, | 
|  | )) { | 
|  |  | 
|  | &cmd_line_err(); | 
|  |  | 
|  | exit 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | my $exit_after_messages = 0; | 
|  |  | 
|  | if ($version) { | 
|  | print STDERR "\n$script_name  $VUFX\n\n"; | 
|  | $exit_after_messages = 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | if ($help) { | 
|  | &usage; | 
|  | $exit_after_messages = 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | if ($show_lists) { | 
|  |  | 
|  | print "\n"; | 
|  | print "These compatibles are hard coded to have no driver.\n"; | 
|  | print "\n"; | 
|  | for my $compat (sort keys %compat_white_list) { | 
|  | print "   $compat\n"; | 
|  | } | 
|  |  | 
|  |  | 
|  | print "\n\n"; | 
|  | print "The driver for these compatibles is hard coded (white list).\n"; | 
|  | print "\n"; | 
|  | my $max_compat_len = 0; | 
|  | for my $compat (sort keys %driver_hard_code_list) { | 
|  | if (length $compat > $max_compat_len) { | 
|  | $max_compat_len = length $compat; | 
|  | } | 
|  | } | 
|  | for my $compat (sort keys %driver_hard_code_list) { | 
|  | if (($driver ne "hardcoded_no_driver") && ($driver ne "no_driver")) { | 
|  | my $first = 1; | 
|  | for my $driver (@{ $driver_hard_code_list{$compat} }) { | 
|  | if ($first) { | 
|  | print "   $compat"; | 
|  | print " " x ($max_compat_len - length $compat); | 
|  | $first = 0; | 
|  | } else { | 
|  | print "   ", " " x $max_compat_len; | 
|  | } | 
|  | print "  $driver\n"; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | print "\n\n"; | 
|  | print "The configuration option for these drivers is hard coded (white list).\n"; | 
|  | print "\n"; | 
|  | my $max_driver_len = 0; | 
|  | for my $driver (sort keys %driver_config_hard_code_list) { | 
|  | if (length $driver > $max_driver_len) { | 
|  | $max_driver_len = length $driver; | 
|  | } | 
|  | } | 
|  | for my $driver (sort keys %driver_config_hard_code_list) { | 
|  | if (($driver ne "hardcoded_no_driver") && ($driver ne "no_driver")) { | 
|  | my $first = 1; | 
|  | for my $config (@{ $driver_config_hard_code_list{$driver} }) { | 
|  | if ($first) { | 
|  | print "   $driver"; | 
|  | print " " x ($max_driver_len - length $driver); | 
|  | $first = 0; | 
|  | } else { | 
|  | print "   ", " " x $max_driver_len; | 
|  | } | 
|  | print "  $config\n"; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | print "\n\n"; | 
|  | print "These drivers are black listed.\n"; | 
|  | print "\n"; | 
|  | for my $driver (@black_list_driver) { | 
|  | print "   $driver\n"; | 
|  | } | 
|  |  | 
|  | print "\n"; | 
|  |  | 
|  | $exit_after_messages = 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | if ($exit_after_messages) { | 
|  | exit 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | $exclude_flag_pattern = "["; | 
|  | for my $exclude_flag (@exclude_flag) { | 
|  | $exclude_flag_pattern = $exclude_flag_pattern . $exclude_flag; | 
|  | } | 
|  | $exclude_flag_pattern = $exclude_flag_pattern . "]"; | 
|  | # clean up if empty | 
|  | $exclude_flag_pattern =~ s/^\[\]$//; | 
|  |  | 
|  |  | 
|  | $include_flag_pattern = "["; | 
|  | for my $include_flag (@include_flag) { | 
|  | $include_flag_pattern = $include_flag_pattern . $include_flag; | 
|  | } | 
|  | $include_flag_pattern = $include_flag_pattern . "]"; | 
|  | # clean up if empty | 
|  | $include_flag_pattern =~ s/^\[\]$//; | 
|  |  | 
|  |  | 
|  | if ($exclude_flag_pattern) { | 
|  | my $found = 0; | 
|  | for $pr_flag_value (@pr_flag_value) { | 
|  | if ($exclude_flag_pattern =~ m/$pr_flag_value/) { | 
|  | $found = 1; | 
|  | } | 
|  | } | 
|  | if (!$found) { | 
|  | &cmd_line_err("invalid value for FLAG in --exclude-flag\n"); | 
|  | exit 1 | 
|  | } | 
|  | } | 
|  |  | 
|  | if ($include_flag_pattern) { | 
|  | my $found = 0; | 
|  | for $pr_flag_value (@pr_flag_value) { | 
|  | if ($include_flag_pattern =~ m/$pr_flag_value/) { | 
|  | $found = 1; | 
|  | } | 
|  | } | 
|  | if (!$found) { | 
|  | &cmd_line_err("invalid value for FLAG in --include-flag\n"); | 
|  | exit 1 | 
|  | } | 
|  | } | 
|  |  | 
|  | if ($include_suspect) { | 
|  | $include_flag_pattern =~ s/\[//; | 
|  | $include_flag_pattern =~ s/\]//; | 
|  | $include_flag_pattern = "[" . $include_flag_pattern . "A-Z]"; | 
|  | } | 
|  |  | 
|  | if ($exclude_flag_pattern =~ m/$include_flag_pattern/) { | 
|  | &cmd_line_err("the same flag appears in both --exclude-flag and --include-flag or --include-suspect\n"); | 
|  | exit 1 | 
|  | } | 
|  |  | 
|  |  | 
|  | # ($#ARGV < 0) is valid for --help, --version | 
|  | if ($#ARGV < 0) { | 
|  | &cmd_line_err("device-tree... is required"); | 
|  | exit 1 | 
|  | } | 
|  |  | 
|  |  | 
|  | if ($config_file) { | 
|  | &read_config_file(); | 
|  | } | 
|  |  | 
|  |  | 
|  | # avoid pushing duplicates for this value | 
|  | $driver = "hardcoded_no_driver"; | 
|  | for $config ( @{ $driver_config_hard_code_list{$driver} } ) { | 
|  | push @{ $driver_config{$driver} }, $config; | 
|  | } | 
|  |  | 
|  | if ($white_list_driver) { | 
|  | for my $compat (keys %driver_hard_code_list) { | 
|  | for my $driver (@{ $driver_hard_code_list{$compat} }) { | 
|  | push @{ $compat_driver{$compat} }, $driver; | 
|  | if ($driver ne "hardcoded_no_driver") { | 
|  | $driver_count{$compat} = scalar @{ $compat_driver{$compat} }; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if ($white_list_config) { | 
|  | for my $driver (keys %driver_config_hard_code_list) { | 
|  | if ($driver ne "hardcoded_no_driver") { | 
|  | for $config ( @{ $driver_config_hard_code_list{$driver} } ) { | 
|  | push @{ $driver_config{$driver} }, $config; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (-x "scripts/dtc/dtx_diff") { | 
|  | $dtx_diff = "scripts/dtc/dtx_diff"; | 
|  | } else { | 
|  |  | 
|  | print STDERR "\n"; | 
|  | print STDERR "$script_name must be run from the root directory of a Linux kernel tree\n"; | 
|  | print STDERR "\n"; | 
|  | exit 3; | 
|  | } | 
|  |  | 
|  | for $file (@ARGV) { | 
|  | &read_dts($file); | 
|  | } |