#!/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 add_parameters --quirk-s3-mode add_parameters --quirk-s3-bios 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 add_parameters --quirk-s3-mode add_parameters --quirk-s3-bios 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