#!/usr/bin/perl # monitor-edid # Copyright (C) 2005-2010 Mandriva # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . use Getopt::Long; my %opt = ('acpi-dir' => "/proc/acpi/video"); my @common_options = ('verbose', 'try-in-console', 'no-vbe', 'acpi-dir=s', 'vbe-port=i', 'max-vbe-port=i'); my $common_options_usage = '[-v] [--acpi-dir ] [--try-in-console] [--vbe-port <0-3>] [--max-vbe-port <0-3>]'; if ($0 =~ /monitor-get-edid/) { GetOptions_(@common_options) or die "usage: monitor-get-edid $common_options_usage\n"; if (my @edids = get_edids(1)) { print $edids[0][1]; exit 0; } else { exit 1; } } else { GetOptions_(@common_options, 'first', 'MonitorsDB', 'perl') or die "usage: monitor-edid $common_options_usage [--first] [--perl] [--MonitorsDB]\n"; my $err = 1; if (my @edids = get_edids($opt{first})) { print "(\n" if $opt{perl}; foreach (@edids) { my ($f, $edid) = @$_; warn "parsing EDID from $f\n" if $opt{verbose}; if ($f =~ m!^/!) { system(parse_edid() . " $f"); $err = 0 if $? == 0; } else { open(my $F, '|' . parse_edid()); print $F $edid; close $F and $err = 0; } print ",\n" if $opt{perl}; } print ")\n" if $opt{perl}; } exit $err; } sub GetOptions_ { my (@l) = @_; GetOptions('v' => \$opt{verbose}, map { $_ => \$opt{/([^=]*)/ && $1} } @l); } sub propagate_options { my (@l) = @_; map { $_ eq 'verbose' ? '-v' : "--$_" } grep { $opt{$_} } @l; } sub get_using_vbe() { my $prog = '/usr/sbin/monitor-get-edid-using-vbe'; -x $prog && join(' ', $prog, propagate_options('verbose', 'try-in-console')); } sub parse_edid() { join(' ', 'monitor-parse-edid', propagate_options('verbose', 'MonitorsDB', 'perl')); } sub get_edids { my ($b_get_first) = @_; my %seen; my @l = map { my $s = slurp($_); (is_edid_possibly_valid($s) && !$seen{ $_[1] }++) ? [ $_ => $s ] : (); } get_edid_files(); # Don't try to run monitor-get-edid-using-vbe when booted in UEFI mode. # It may overwrite a memory region being used by the UEFI BIOS (mga#28124) return @l if -e '/sys/firmware/efi'; if (!@l || !$b_get_first && $< == 0) { if (my $cmd = get_using_vbe()) { my $min_port = $opt{'vbe-port'} || 0; my $max_port = $opt{'max-vbe-port'} || $opt{'vbe-port'} || 2; my $skip_vbe; foreach my $port ($min_port .. $max_port) { warn "probing EDID using VBE (port $port)\n" if $opt{verbose}; my $edid = `$cmd --port $port $skip_vbe`; my $status = $? >> 8; if ($status == 1) { warn "VBE info call failed, skipping all ports\n" if $opt{verbose}; last; } # skip VBE check for other ports if it was OK $skip_vbe = "--skip-vbe-check" if $status == 0 || $status == 2; is_edid_possibly_valid($edid) or next; next if grep { $_->[1] eq $edid } @l; push @l, [ "vbe$port" => $edid ]; last if $b_get_first; } } } @l; } sub get_edid_files() { my @l1 = find_EDID("/proc/device-tree"); my @l2 = grep { (my $state_f = $_) =~ s/EDID$/state/; my ($state) = slurp($state_f) =~ /state:\s*0x(\w+)/; hex($state) & 2; # bit 1 is "Output is activated" } find_EDID($opt{'acpi-dir'}); my @l3 = grep { (my $state_f = $_) =~ s/edid$/enabled/; slurp($state_f) =~ /enabled/; } glob("/sys/class/drm/card*-*/edid"); (@l1, @l2, @l3); } sub find_EDID { my ($dir) = @_; my @l; require File::Find; File::Find::find(sub { $_ eq 'EDID' and push @l, $File::Find::name }, $dir); @l; } sub is_edid_possibly_valid { my ($edid) = @_; length($edid) >= 128; } sub slurp { my ($f) = @_; open(my $F, '<', $f) or return; local $/; scalar <$F>; }