diff options
Diffstat (limited to 'pm/sleep.d/98video-quirk-db-handler')
-rwxr-xr-x | pm/sleep.d/98video-quirk-db-handler | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/pm/sleep.d/98video-quirk-db-handler b/pm/sleep.d/98video-quirk-db-handler new file mode 100755 index 0000000..942a6a9 --- /dev/null +++ b/pm/sleep.d/98video-quirk-db-handler @@ -0,0 +1,437 @@ +#!/bin/bash +# Prototype video quirk database handler that does not rely on HAL. + +shopt -s extglob + +. "${PM_FUNCTIONS}" + +[[ $PM_DEBUG ]] && { + export PS4='${BASH_SOURCE}@${LINENO}(${FUNCNAME[0]}): '; + set -x +} + +possible_video_quirks=" --quirk-dpms-on + --quirk-dpms-suspend + --quirk-s3-mode + --quirk-s3-bios + --quirk-vbe-post + --quirk-vbe-post + --quirk-vga-mode-3 + --quirk-vbemode-restore + --quirk-vbestate-restore + --quirk-reset-brightness + --quirk-radeon-off + --quirk-no-fb + --quirk-save-pci" + +possible_system_properties="system.firmware.version + system.firmware.vendor + system.firmware.release_date + system.hardware.vendor + system.hardware.product + system.hardware.version + system.board.product + system.board.version + system.board.vendor + system.hardware.primary_video.vendor + system.hardware.primary_video.product + system.hardware.primary_video.driver + system.hardware.primary_video.using_kms + system.kernel.version" + +has_video_parameters() { + local p params=$(get_parameters) + [[ $params ]] || return 1 + for p in $params; do + [[ $possible_video_quirks = *$p* ]] && return + done + return 1 +} + +# video specific helper functions. + +# Are we using the nVidia binary driver? +using_nvidia() { [[ -d /sys/module/nvidia ]]; } + +# How about the ATI one? +using_fglrx() { [[ -d /sys/module/fglrx ]]; } + +# OK, what about a driver that is using kernel modesetting? +using_kms() { grep -q -E '(nouveau|drm)fb' /proc/fb; } + +# Get some video related values when HAL has not gotten them for us, or +# HAL is not available. +videoget() { + local dev pci + pci="/sys/bus/pci/devices" + for dev in "$pci"/*; do + [[ -f "${dev}/class" ]] || continue + [[ "$(cat "${dev}/class")" = "0x030000" ]] || continue + case $1 in + vendor) RES="$(cat "${dev}/vendor")" ;; + device) RES="$(cat "${dev}/device")" ;; + driver) + if [[ -L ${dev}/driver ]]; then + RES="$(readlink "${dev}/driver")" + RES="${RES##*/}" + elif using_nvidia; then + RES=nvidia + elif using_fglrx; then + RES=fglrx + fi + ;; + using_kms) + if using_kms; then + RES=true + else + RES=false + fi + ;; + esac + break + done +} + +# Get some important information about this system. + +# If we have /sys/class/dmi/id, life is easy, and we do not need to +# depend on HAL or dmidecode. +_dmisysget() { + [[ -r /sys/class/dmi/id/$1 ]] || RES="" + read RES < "/sys/class/dmi/id/$1" +} + +dmisysget() { + case $1 in + system.firmware.vendor) _dmisysget bios_vendor ;; + system.firmware.version) _dmisysget bios_version ;; + system.firmware.release_date) _dmisysget bios_date ;; + system.hardware.vendor) _dmisysget sys_vendor ;; + system.hardware.product) _dmisysget product_name ;; + system.hardware.version) _dmisysget product_version ;; + system.board.product) _dmisysget board_name ;; + system.board.version) _dmisysget board_version ;; + system.board.vendor) _dmisysget board_vendor ;; + system.hardware.primary_video.vendor) videoget vendor ;; + system.hardware.primary_video.product) videoget device ;; + system.hardware.primary_video.driver) videoget driver ;; + system.hardware.primary_video.using_kms) videoget using_kms ;; + system.kernel.version) RES=$(uname -r) ;; + *) return 1 + esac +} + +# Get system information using dmidecode. Slow and ugly, but +# should be supported just about everywhere. +_dmidecodeget() { + RES=$(dmidecode -s $1) +} + +dmidecodeget() { + case $1 in + system.firmware.vendor) _dmidecodeget bios-vendor ;; + system.firmware.version) _dmidecodeget bios-version ;; + system.firmware.release_date) _dmidecodeget bios-release-date;; + system.hardware.vendor) _dmidecodeget system-manufacturer;; + system.hardware.product) _dmidecodeget system-product-name;; + system.hardware.version) _dmidecodeget system-version;; + system.board.product) _dmidecodeget baseboard-product-name;; + system.board.version) _dmidecodeget baseboard-version;; + system.board.vendor) _dmidecodeget baseboard-manufacturer;; + *) return 1 + esac +} + +# If we have HAL, it has already done most of the work for us. +halget() { + local hgp="hal-get-property --udi /org/freedesktop/Hal/devices/computer --key" + case $1 in + system.firmware.version) RES=$($hgp "$1") ;; + system.firmware.vendor) RES=$($hgp "$1") ;; + system.firmware.release_date) RES=$($hgp "$1") ;; + system.hardware.vendor) RES=$($hgp "$1") ;; + system.hardware.product) RES=$($hgp "$1") ;; + system.hardware.version) RES=$($hgp "$1") ;; + system.board.product) RES=$($hgp "$1") ;; + system.board.version) RES=$($hgp "$1") ;; + system.board.vendor) RES=$($hgp "$1") ;; + *) return 1 + esac +} + +canonicalize_dmivar() { + [[ $1 =~ ^[a-z._-]+$ && $possible_system_properties = *$1* ]] || return 1 + echo "${1//[-.]/_}" +} + +# Precache the DMI and other information we will need as shell variables. +# This will make things easier when we start running for real. +precache_dmivars() { + local p q f + for q in $possible_system_properties; do + p=$(canonicalize_dmivar $q) || return 1 + RES="" + for f in dmisysget halget dmidecodeget; do + "$f" "$q" && break || continue 2 + done + RES="${RES##+( )}" + RES="${RES%%+( )}" + read "$p" <<<$RES + done + RES="" +} + +# Use bash variable indirection to set RES to the actual parameter +# we are looking for. Sanity check the variable we were passed to +# keep things sane. +getprop() { + RES="" + local p + if ! p=$(canonicalize_dmivar $1); then + echo "Unable to obtain DMI information for $1" >&2 + exit 1 + fi + RES="${!p}" +} + +# test to see if the parameter passed is a decimal or hexidecimal number +# Note the complete lack of floating point support. +isnum() { + [[ $1 =~ ^[0-9]+\$ || $1 =~ ^0[xX][0-9a-fA-F]+\$ ]] +} + +# for all the matching functions, +# $2 = the given constant (or regular expression), +# $1 = the raw data grabbed from HAL or dmidecode or wherever + +regex() { [[ $1 =~ ${2//;/|} ]]; } + +regex_ncase() { + local r + shopt -s nocasematch + regex "$1" "$2" + r=$? + shopt -u nocasematch + return $r +} + +regex_inverse() { ! regex "$1" "$2"; } +compare_eq() { [[ $1 = $2 ]]; } +compare_ne() { [[ $1 != $2 ]]; } +compare_gt() { [[ $1 > $2 ]]; } +compare_ge() { compare_eq "$@" || compare_gt "$@"; } +compare_lt() { [[ $1 < $2 ]]; } +compare_le() { compare_eq "$@" || compare_lt "$@"; } +numeric_compare_eq() { (( $1 == $2 )); } +numeric_compare_ne() { (( $1 != $2 )); } +numeric_compare_gt() { (( $1 > $2 )); } +numeric_compare_ge() { (( $1 >= $2 )); } +numeric_compare_lt() { (( $1 < $2 )); } +numeric_compare_le() { (( $1 <= $2 )); } +numeric_compare_eq_list() { + local key val + # $1 = key val to compare + # $2 = list to compare to + key=$1 + val="${2//;/ }" + for x in $val; do + (( $key == $x )) && return 0 + done + return 1 +} + +# Helper function for nVidia g80 gpus. They require extra special handling +# when not using the nVidia binary driver. +have_nvidia_g80() { + numeric_compare_eq $system_hardware_primary_video_vendor 0x10de || return + numeric_compare_eq_list $system_hardware_primary_video_product \ + '0x190;0x191;0x192;0x193;0x194;0x195;0x196;0x197;0x198;0x199;0x19a;0x19b;0x19c;0x19d;0x19e;0x19f;0x400;0x401;0x402;0x403;0x404;0x405;0x406;0x407;0x408;0x409;0x40a;0x40b;0x40c;0x40d;0x40e;0x40f;0x420;0x421;0x422;0x423;0x424;0x425;0x426;0x427;0x428;0x429;0x42a;0x42b;0x42c;0x42d;0x42e;0x42f' || return +} + +# Helper function for recent Intel framebuffer drivers. +have_smart_intel() { + local kernel_rev=$system_kernel_revision + local driver=$system_hardware_primary_video_driver + # currently, intel kernel modesetting is not quite smart enough + # we still need acpi s3 kernel modesetting hooks, so don't remove those + # options if they were passed. + [[ $driver = i915 && ( $kernel_rev > 2.6.26 || $kernel_rev = 2.6.26 ) ]] +} + +# find the appropriate quirks for this system using the native-format +# quirks database. Since the database is tree-ish, we use some stupid +# recursion tricks. + +# $1 = whether to ignore what we are reading +_find_native() { + local action key matcher regex + while read action key matcher regex; do + [[ $action && ${action:0:1} != '#' ]] || continue + case $action in + match) + if [[ $1 = ignore ]]; then + _find_native $1 + else + getprop "$key" + [[ $matcher && $regex ]] || find_native ignore + # if this matcher matches, look at nodes farther out. + if $matcher "$RES" "$regex"; then + _find_native work + else + _find_native ignore + fi + fi + ;; + endmatch) + [[ $found ]] && return 0 || return 1 ;; + addquirk) [[ $1 = ignore ]] && continue + found=true + add_parameters "$key" + ;; + delquirk) [[ $1 = ignore ]]&& continue + found=true + remove_parameters "$key" + ;; + esac + done +} + +find_native() ( + [[ -f $1 ]] || return 1 + exec <"$1" + _find_native work + res=$? + get_parameters + return $res +) + +# If we resumed, write out the quirks we used as our last known +# working ones for this hardware, kernel, driver, and KMS setting. +write_last_known_working() ( + local matcher quirk + precache_dmivars + exec >"$PM_LKW_QUIRKS" + for prop in system.firmware.version system.firmware.vendor \ + system.firmware.release_date system.hardware.vendor \ + system.hardware.product system.hardware.version \ + system.board.product system.board.version system.board.vendor \ + system.hardware.primary_video.vendor \ + system.hardware.primary_video.product \ + system.hardware.primary_video.driver \ + system.hardware.primary_video.using_kms \ + system.kernel.version; do + getprop "$prop" + if isnum "$RES"; then + matcher=numeric_compare_eq + else + matcher=compare_eq + fi + echo "match $prop $matcher ${RES}" + done + if [[ $QUIRKS ]]; then + for quirk in $QUIRKS; do + echo "addquirk $quirk" + done + else + echo "addquirk --quirk-none" + fi + for ((x=1; x<14; x++)); do + echo endmatch + done +) + +case $1 in + suspend|hibernate) + # Aaand.... GO + # cache all the properties we will need. + precache_dmivars + + # This logic can also be expressed using entries in the quirkdb, + # but I am too lazy to do that until a final quirk database is + # formalized. + if has_parameter --quirk-test && has_video_parameters; then + # The user is explicitly testing video parameters. + # Use them without the usual filtering. This may cause the system + # to blow up, but they explicitly asked for it. + remove_parameters --quirk-test + elif using_kms; then + # Using kernel modesetting? No quirks, and do not change vts. + remove_parameters $possible_video_quirks + add_parameters --quirk-no-chvt + elif using_nvidia; then + # Ditto for nVidia binary drivers + remove_parameters $possible_video_quirks + elif using_fglrx; then + # fglrx may or may not have to change vts, reports one + # way or the other welcome. + remove_parameters $possible_video_quirks + add_parameters --quirk-none + elif have_nvidia_g80; then + # nVidia G80 GPUs require special handling when not using nvidia + # binary drivers. I do not know if noveau requires help or not. + remove_parameters $possible_video_quirks + add_parameters --quirk-vbe-post + else + # Go ahead and get our quirks. + if has_video_parameters; then + # Parameters from the command line take precedence + # over the database, so do not query it. + : + elif [[ $PM_QUIRKS ]]; then + # If we have $PM_QUIRKS. use it instead of the quirk database + add_parameters $PM_QUIRKS + # If we were not passed any quirks on the command line, + # get them from the database. + elif QUIRKS=$(find_native "$PM_LKW_QUIRKS"); then + # Known working quirks from our last run are still valid. + # Use them. + add_parameters $QUIRKS + else + # Our known working quirks from the last run are either + # nonexistent or invalid. Either way, start over. + rm "$PM_LKW_QUIRKS" >/dev/null 2>&1 + for f in "$PM_QUIRKDB"/*.quirkdb + do + QUIRKS=$(find_native "$f") && break + done + # some default quirks if we did not get any. + [[ -z $QUIRKS ]] && QUIRKS="--quirk-vbe-post + --quirk-dpms-on --quirk-dpms-suspend + --quirk-vbestate-restore + --quirk-vbemode-restore + --quirk-vga-mode-3" + add_parameters $QUIRKS + savestate video_quirks "$QUIRKS" + fi + if have_smart_intel; then + # Intel without KMS does not require most quirks, no matter + # what anything else says. The only ones that seem to + # matter are the --quirk-s3 ones, so remove everything else. + remove_parameters --quirk-dpms-on \ + --quirk-dpms-suspend \ + --quirk-vbe-post \ + --quirk-vbe-post \ + --quirk-vga-mode-3 \ + --quirk-vbemode-restore \ + --quirk-vbestate-restore \ + --quirk-reset-brightness \ + --quirk-radeon-off \ + --quirk-no-fb \ + --quirk-save-pci + fi + fi + ;; + thaw|resume) + if state_exists video_quirks; then + QUIRKS=$(restorestate video_quirks); + write_last_known_working + elif has_parameter --store-quirks-as-lkw; then + for x in $(get_parameters); do + for y in $possible_video_quirks; do + [[ $x = $y ]] && QUIRKS=" $QUIRKS $x" + done + done + write_last_known_working + fi + ;; +esac |