lkml.org 
[lkml]   [2016]   [Apr]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH] scripts/dtc: dt_to_config - report kernel config options for a devicetree
    From: Frank Rowand <frank.rowand@am.sony.com>

    Determining which kernel config options need to be enabled for a
    given devicetree can be a painful process. Create a new tool to
    find the drivers that may match a devicetree node compatible,
    find the kernel config options that enable the driver, and
    optionally report whether the kernel config option is enabled.

    Signed-off-by: Gaurav Minocha <gaurav.minocha.os@gmail.com>
    Signed-off-by: Frank Rowand <frank.rowand@am.sony.com>

    ---
    scripts/dtc/dt_to_config | 1061 +++++++++++++++++++++++++++++++++++++++++++++++
    1 file changed, 1061 insertions(+)

    Index: b/scripts/dtc/dt_to_config
    ===================================================================
    --- /dev/null
    +++ b/scripts/dtc/dt_to_config
    @@ -0,0 +1,1061 @@
    +#!/usr/bin/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 = "160428a";
    +
    +$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 = (
    + 'fixed-partitions' => '1',
    + 'none' => '1',
    + 'pci' => '1',
    + 'simple-bus' => '1',
    +);
    +
    +# magic compatibles, have a driver
    +#
    +# Will not search for drivers for these compatibles.
    +# Will instead use the drivers and config options listed here.
    +#
    +# If you add an entry to this hash, add the corresponding entry
    +# to %driver_config_hard_code_list.
    +#
    +# 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!
    +#
    +# TODO: 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'],
    + 'gpios' => ['drivers/leds/leds-tca6507.c'],
    + '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'],
    +);
    +
    +%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/leds/leds-tca6507.c' => ['CONFIG_LEDS_TCA6507'],
    + 'drivers/input/keyboard/gpio_keys.c' => ['CONFIG_KEYBOARD_GPIO'],
    + 'drivers/i2c/busses/i2c-gpio.c' => ['CONFIG_I2C_GPIO'],
    + 'arch/mips/mti-malta/malta-dt.c' => ['obj-y'],
    + 'arch/x86/kernel/devicetree.c' => ['CONFIG_OF'],
    + 'drivers/mtd/maps/physmap_of.c' => ['CONFIG_MTD_PHYSMAP_OF'],
    + 'drivers/video/backlight/pwm_bl.c' => ['CONFIG_BACKLIGHT_PWM'],
    + 'drivers/mfd/syscon.c' => ['CONFIG_MFD_SYSCON'],
    +
    + # 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
    +# driver_black_list.
    +
    +@driver_black_list = (
    + 'virt/kvm/arm/.*',
    +);
    +
    +
    +sub usage()
    +{
    + print STDERR
    +"
    +Usage: $script_name [options] device-tree...
    +
    + device_tree is: dts_file | dtb_file | proc_device-tree
    +
    +
    +Valid options:
    + -b ignore driver black list
    + -c FILE Read kernel config options from FILE
    + --config FILE synonym for 'c'
    + --exclude-flag FLAG exclude entries with a matching flag
    + -h Display this message and exit
    + --help synonym for 'h'
    + --include-flag FLAG include only entries with a matching flag
    + --include-suspect include only entries with an uppercase flag
    + --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.
    +
    + CAUTION:
    + This program uses heuristics to guess which driver(s) support each
    + compatible string and which config option 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 STDERR " %s %s\n",
    + $pr_flag_value[$k], $pr_flag_help[$k];
    + }
    +
    + print STDERR
    +"
    + Upper case letters indicate potential issues or problems.
    +
    +
    + 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)
    +
    +";
    +}
    +
    +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 $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);
    + if ($not) {
    + &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;
    + }
    +
    + 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 $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;
    + }
    +
    + print "$node : $compat : $driver : $config : ";
    +
    + if ($config_file) {
    + my @configs = split(' && ', $config);
    + for $configs (@configs) {
    + $configs =~ s/^!//;
    + my $existing_config = $existing_config{$configs};
    + if (!$existing_config) {
    + # check for /-m/, /-y/, or /-objs/
    + if (($configs !~ /CONFIG_/) &&
    + ($configs ne "no_config") &&
    + ($configs ne "no_makefile")) {
    + $existing_config = "x";
    + };
    + };
    + if ($existing_config) {
    + print "$separator", "$existing_config";
    + $separator = ", ";
    + } else {
    + print "$separator", "n";
    + $separator = ", ";
    + }
    + }
    + } else {
    + print "none";
    + }
    +
    + print "\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 $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{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);
    + }
    + }
    + 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 (!$ignore_driver_black_list) {
    + for $black (@driver_black_list) {
    + 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 (! `which dtx_diff`) {
    + print STDERR "\n";
    + print STDERR "file 'dtx_diff' is not executable or does not exist\n";
    + print STDERR " Is scripts/dtc/ in the shell \$PATH variable?\n";
    + print STDERR " Are you in the root directory of a kernel tree?\n";
    + print STDERR "\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($node, $compatible, $node_enabled);
    +
    + $node = $line;
    + $node =~ s/^\s*(.*)\s+\{.*/$1/;
    + $node =~ s/.*: //;
    +
    + $compatible = "";
    + $node_enabled = "";
    + next FILE;
    + }
    +
    + 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($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(
    + "b" => \$ignore_driver_black_list,
    + "c=s" => \$config_file,
    + "h" => \$help,
    + "config=s" => \$config_file,
    + "exclude-flag=s" => \@exclude_flag,
    + "help" => \$help,
    + "include-flag=s" => \@include_flag,
    + "include-suspect" => \$include_suspect,
    + "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 white listed 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.\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")) {
    + for my $driver (@{ $driver_hard_code_list{$compat} }) {
    + print " $compat";
    + print " " x ($max_compat_len - length $compat);
    + print " $driver\n";
    + }
    + }
    + }
    +
    +
    + print "\n\n";
    + print "The configuration option for these drivers is hard coded.\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")) {
    + for my $config (@{ $driver_config_hard_code_list{$driver} }) {
    + print " $driver";
    + print " " x ($max_driver_len - length $driver);
    + print " $config\n";
    + }
    + }
    + }
    +
    +
    + print "\n\n";
    + print "These compatibles are black listed. They will not be reported without '-b'.\n";
    + print "\n";
    + for my $driver (@driver_black_list) {
    + 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;
    +}
    +
    +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} };
    + }
    + }
    +}
    +
    +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;
    + }
    + }
    +}
    +
    +
    +for $file (@ARGV) {
    + &read_dts($file);
    +}
    \
     
     \ /
      Last update: 2016-04-29 00:01    [W:4.293 / U:0.028 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site