Skip to content

Instantly share code, notes, and snippets.

@startergo
Forked from joevt/EDIDUtil.sh
Created November 30, 2020 01:31
Show Gist options
  • Select an option

  • Save startergo/ba2f4d733b9e9452283cc7f359d1ad25 to your computer and use it in GitHub Desktop.

Select an option

Save startergo/ba2f4d733b9e9452283cc7f359d1ad25 to your computer and use it in GitHub Desktop.
A set of shell functions used to view and edit EDIDs.
# EDIDUtil.sh v5.12
# by joevt Jun 27/2020
#=========================================================================================
# Modify EDID
getarrstart () {
# bash arrays start at 0
# zsh arrays start at 1 (applies only to [] syntax) but this can be changed with "setopt ksh_arrays"
# zsh arrays start at 0 when using ${arr:x:x} syntax
local arr=(1 0)
arrstart=${arr[1]}
}
getarrstart
replacebytes () {
local bytepos=$(($1*2))
local thebytes=$2
local thelen=${#thebytes}
[[ -n $3 ]] && thelen=$(($3*2))
theedid=${theedid:0:$bytepos}${thebytes}${theedid:$bytepos+thelen}
}
repairchecksums () {
local blockoffset=0
while (( blockoffset*2 < ${#theedid} )); do
local blocktag=${theedid:$blockoffset*2:2}
if [[ $blocktag = 70 ]]; then
local sectionsize=$((0x${theedid:$blockoffset*2+4:2}))
replacebytes $(($blockoffset+$sectionsize+5)) $(printf "%02x" $(( -($(echo -n ${theedid:$blockoffset*2+2:$sectionsize*2+8} | sed "s/../+0x&/g")) & 0xff )))
fi
replacebytes $(($blockoffset+127)) $(printf "%02x" $(( -($(echo -n ${theedid:$blockoffset*2:254} | sed "s/../+0x&/g")) & 0xff )))
((blockoffset+=128))
done
}
deleteextensionblock () {
local blockoffset=$(($1*128))
if (( blockoffset*2 < ${#theedid} )); then
(( debug )) && echo ": deleteextensionblock $blockoffset" 1>&2
replacebytes $blockoffset "" 128
replacebytes 126 $(printf "%02x" $((0x${theedid:252:2}-1)))
processedid
repairchecksums
else
echo "Block at $blockoffset doesn't exist"
fi
}
deleteextensionblocktype () {
local blocktype="$1"
local blockoffset=128
local blocknum=1
while (( blockoffset*2 < ${#theedid} )); do
local blocktag=${theedid:$blockoffset*2:2}
(( debug )) && echo ": deleteextensionblocktype $blocknum) $blockoffset $blocktag" 1>&2
if (( 0x$blocktag == $blocktype )); then
deleteextensionblock $blocknum
else
((blockoffset+=128))
((blocknum++))
fi
done
}
adddisplayidextensionblock () {
local version="$1"
local producttype="$2"
(( debug )) && echo ": adddisplayidextensionblock $version $producttype" 1>&2
#echo adddisplayidextensionblock $version $producttype
replacebytes 126 $(printf "%02x" $((0x${theedid:252:2}+1)))
theedid+=$(printf "70%02x79%02x%0248x" $((0x${version//./})) "$producttype" 0)
repairchecksums
}
addctaextensionblock () {
local version="$1"
(( debug )) && echo ": addctaextensionblock $version" 1>&2
#echo addctaextensionblock $version $producttype
replacebytes 126 $(printf "%02x" $((0x${theedid:252:2}+1)))
theedid+=$(printf "02%02x0400%0248x" $((${version})) 0)
repairchecksums
}
detailed_block () {
local detailedblock=$1
# other parameters are optional
local detailedblockoffset=$2
local willbereplaced=$3
[[ -z $willbereplaced ]] && willbereplaced=1
local dodump=$dodump
if (( $# == 1 )); then
dodump=1
fi
if [[ ${detailedblock:0:4} = 0000 ]]; then
case ${detailedblock} in
000000000000000000000000000000000000) (( dodump )) && printf "Empty" ;;
000000100000000000000000000000000000) (( dodump )) && printf "Dummy block" ;;
*)
case ${detailedblock:6:2} in
0*) (( dodump )) && printf "Manufacturer-specified data" ;;
10) (( dodump )) && printf "Dummy block" ;;
f7) (( dodump )) && printf "Established timings III" ;;
f8) (( dodump )) && printf "CVT 3-byte timing codes" ;;
f9) (( dodump )) && printf "Color management data" ;;
fa) (( dodump )) && printf "More standard timings" ;;
fb) (( dodump )) && printf "Color point" ;;
fc) (( dodump )) && printf "Monitor name" ;;
fd) (( dodump )) && printf "Display range limits" ;;
fe) (( dodump )) && printf "ASCII string" ;;
ff) (( dodump )) && printf "Serial number" ;;
*) (( dodump )) && printf "Reserved(${detailedblock:6:2})" ;;
esac
(( dodump )) && printf ": $detailedblock"
esac
case ${detailedblock:6:2} in
fd)
local minV=$((((0x${detailedblock:8:2} >> 0) & 1) * 255 + 0x${detailedblock:10:2}))
local maxV=$((((0x${detailedblock:8:2} >> 1) & 1) * 255 + 0x${detailedblock:12:2}))
local minH=$((((0x${detailedblock:8:2} >> 2) & 1) * 255 + 0x${detailedblock:14:2}))
local maxH=$((((0x${detailedblock:8:2} >> 3) & 1) * 255 + 0x${detailedblock:16:2}))
local maxP=$((0x${detailedblock:18:2} * 10))
local timingtype="?"
case $((0x${detailedblock:20:2})) in
0) timingtype="default GTF" ;;
1) timingtype="limits only" ;;
2) timingtype="Secondary GTF" ;;
4) timingtype="CVT" ;;
*) timingtype="Reserved($((0x${detailedblock:20:2})))" ;;
esac
(( dodump )) && printf " = %d-%dHz %d-%dkHz %dMHz (%s:%s)\n" $minV $maxV $minH $maxH $maxP $timingtype ${detailedblock:22}
;;
*) (( dodump )) && echo
;;
esac
else
local MHz=00$((0x${detailedblock:2:2}${detailedblock:0:2}))
local hmm=$((0x${detailedblock:28:1}${detailedblock:24:2}))
local vmm=$((0x${detailedblock:29:1}${detailedblock:26:2}))
local hactive=$((0x${detailedblock:8:1}${detailedblock:4:2}))
local hfront=$((((0x${detailedblock:22:1} >> 2) << 8) + 0x${detailedblock:16:2}))
local hpulse=$((((0x${detailedblock:22:1} & 3) << 8) + 0x${detailedblock:18:2}))
local hblank=$((0x${detailedblock:9:1}${detailedblock:6:2}))
local vactive=$((0x${detailedblock:14:1}${detailedblock:10:2}))
local vfront=$((((0x${detailedblock:23:1} >> 2) << 4) + 0x${detailedblock:20:1}))
local vpulse=$((((0x${detailedblock:23:1} & 3) << 4) + 0x${detailedblock:21:1}))
local vblank=$((0x${detailedblock:15:1}${detailedblock:12:2}))
local hborder=$((0x${detailedblock:30:2}))
local vborder=$((0x${detailedblock:32:2}))
local hsync='-'
local vsync='-'
local interlaced=''
local hback=$((hblank - hfront - hpulse))
local vback=$((vblank - vfront - vpulse))
(( 0x${detailedblock:34:2} >> 7 )) && interlaced='i'
(( 0x${detailedblock:34:2} & 2 )) && hsync='+'
(( 0x${detailedblock:34:2} & 4 )) && vsync='+'
local kHz='000'
local Hz='000'
if (( hactive + hblank )); then
kHz=000$(((10#$MHz * 100000 / (hactive+hblank) + 5)/10))
fi
if (( (vactive+vblank)*(hactive+hblank) )); then
Hz=000$(((10#$MHz * 100000000 / ((vactive+vblank)*(hactive+hblank)) + 5)/10))
fi
local timingstring=$(
printf "%dx%d%s@%d.%sHz %d.%skHz %d.%sMHz h(%d %d %d %s) v(%d %d %d %s)" \
$hactive $vactive "$interlaced" \
$((10#$Hz/1000)) ${Hz: -3} $((10#$kHz/1000)) ${kHz: -3} $((10#$MHz/100)) ${MHz: -2} \
$hfront $hpulse $hback "$hsync" \
$vfront $vpulse $vback "$vsync"
)
(( debug )) && echo ":(( $willbereplaced == 0 ))" 1>&2
if (( willbereplaced == 0 )); then
thetimings+=("$timingstring")
thetimingsoffsets+=("$detailedblockoffset")
thetimingshex+=("$detailedblock")
fi
(( dodump )) && printf "Detailed timing: %s = %s\n" "$detailedblock" "$timingstring"
fi
}
one_detailed_block () {
(( debug )) && echo ": one_detailed_block $@" 1>&2
local detailedblockoffset=$1
local willbereplaced=$2
(( dodump )) && echo -n " ${detailedblockoffset}) $detailedblockstring"
detailed_block ${theedid:$1*2:36} $detailedblockoffset $willbereplaced
}
detailed_blocks () {
(( debug )) && echo ": detailed_blocks $@" 1>&2
for param in detailedblockoffset lastdetailedblockoffset dodump doreplacedescriptoroffset newdescriptor; do
(( debug )) && echo ": param $param=$1" 1>&2
eval "local $param=\"$1\""; shift
done
(( dodump )) && echo " Descriptors:"
while (( detailedblockoffset <= lastdetailedblockoffset )); do
(( debug )) && echo one_detailed_block $detailedblockoffset $(( detailedblockoffset == doreplacedescriptoroffset )) 1>&2
one_detailed_block $detailedblockoffset $(( detailedblockoffset == doreplacedescriptoroffset ))
if (( detailedblockoffset == doreplacedescriptoroffset )); then
replacebytes $detailedblockoffset $newdescriptor
(( dodump )) && echo " changed:"
one_detailed_block $detailedblockoffset 0
fi
((detailedblockoffset+=18))
done
}
colorc () {
local ccc=$1
# [0..1023]
# [0..9990]
# [10005..19995] add leading zeros (ignore the 1) and rounding
local rx=$((
(
(
(0x${theedid:54+ccc*2:2} << 2) | ((0x${theedid:50:4} >> (14-ccc*2)) & 3)
) * 10000 / 1024
) + 10005
));
echo -n 0.${rx:1:3} # output 3 digits (ignoring the leading 1)
}
dumptype1timingdescriptor () {
local type1timing=$1
local MHz=00$((0x${type1timing:4:2}${type1timing:2:2}${type1timing:0:2} + 1))
local preferred=''
(( 0x${type1timing:6:2} >> 7 )) && preferred=' preferred'
local support3D=' 3D:undefined'
case $(( (0x${type1timing:6:2} >> 5) & 3 )) in
0) support3D='' ;;
1) support3D=' stereo' ;;
2) support3D=' stereo/mono' ;;
3) support3D=' 3D:reserved' ;;
esac
local interlaced=''
(( (0x${type1timing:6:2} >> 4) & 1 )) && interlaced='i'
local aspect=' aspect:reserved'
case $(( 0x${type1timing:6:2} & 15 )) in
0) aspect=' 1:1' ;;
1) aspect=' 5:4' ;;
2) aspect=' 4:3' ;;
3) aspect=' 15:9' ;;
4) aspect=' 16:9' ;;
5) aspect=' 16:10' ;;
6) aspect=' 64:27' ;;
7) aspect=' 256:135' ;;
8) aspect=' aspect:undefined' ;;
esac
local hactive=$((0x${type1timing:10:2}${type1timing:8:2} + 1))
local hblank=$((0x${type1timing:14:2}${type1timing:12:2} + 1))
local hfront=$(((0x${type1timing:18:2}${type1timing:16:2} & 32767) + 1))
local hsync='-'
(( 0x${type1timing:18:2} >> 7 )) && hsync='+'
local hpulse=$((0x${type1timing:22:2}${type1timing:20:2} + 1))
local hback=$((hblank - hfront - hpulse))
local vactive=$((0x${type1timing:26:2}${type1timing:24:2} + 1))
local vblank=$((0x${type1timing:30:2}${type1timing:28:2} + 1))
local vfront=$(((0x${type1timing:34:2}${type1timing:32:2} & 32767) + 1))
local vsync='-'
(( 0x${type1timing:34:2} >> 7 )) && vsync='+'
local vpulse=$((0x${type1timing:38:2}${type1timing:36:2} + 1))
local vback=$((vblank - vfront - vpulse))
local kHz=000$(((10#$MHz * 100000 / (hactive+hblank) + 5)/10))
local Hz=000$(((10#$MHz * 100000000 / ((vactive+vblank)*(hactive+hblank)) + 5)/10))
printf "%dx%d%s@%d.%sHz %d.%skHz %d.%sMHz h(%d %d %d %s) v(%d %d %d %s)%s%s%s" \
$hactive $vactive "$interlaced" \
$((10#$Hz/1000)) ${Hz: -3} $((10#$kHz/1000)) ${kHz: -3} $((10#$MHz/100)) ${MHz: -2} \
$hfront $hpulse $hback "$hsync" \
$vfront $vpulse $vback "$vsync" \
"$support3D" "$aspect" "$preferred"
}
dumptileddisplaytopologyblock () {
local tiledblock=$1
local revision=$((0x${tiledblock:2:2} & 7)) # high 5 bits should be 0
local length=$((0x${tiledblock:4:2})) # should be 22
local capabilities=$((0x${tiledblock:6:2}))
local enclosuretype=$(( (capabilities >> 7) & 1 ))
local tilebezelinformationdescriptoravailable=$(( (capabilities >> 6) & 1 ))
# bit 5 reserved
local behaviorsome=$(( (capabilities >> 3) & 3 ))
local behavioronly=$(( (capabilities >> 0) & 7 ))
tileCountH=$(( 0x${tiledblock:8:1} + ((0x${tiledblock:12:2} >> 2) & 0x30) + 1 ))
tileCountV=$(( 0x${tiledblock:9:1} + ((0x${tiledblock:12:2} >> 0) & 0x30) + 1 ))
local tileLocationH=$(( 0x${tiledblock:10:1} + ((0x${tiledblock:12:2} << 2) & 0x30) ))
local tileLocationV=$(( 0x${tiledblock:11:1} + ((0x${tiledblock:12:2} << 4) & 0x30) ))
tileSizeH=$(( 0x${tiledblock:16:2}${tiledblock:14:2} + 1 ))
tileSizeV=$(( 0x${tiledblock:20:2}${tiledblock:18:2} + 1 ))
# depends on tilebezelinformationdescriptoravailable
local pixelmultiplier=$(( 0x${tiledblock:22:2} ))
local bezelsizetop=$(( 0x${tiledblock:24:2} ))
local bezelsizebottom=$(( 0x${tiledblock:26:2} ))
local bezelsizeleft=$(( 0x${tiledblock:28:2} ))
local bezelsizeright=$(( 0x${tiledblock:30:2} ))
local vendorid=$(echo -n ${tiledblock:32:6} | xxd -p -r)
local productid=$((0x${tiledblock:40:2}${tiledblock:38:2}))
local serialnumber=$((0x${tiledblock:48:2}${tiledblock:46:2}${tiledblock:44:2}${tiledblock:42:2}))
if (( dodump )); then
printf "%dx%d @ (%d,%d) of (%dx%d) vendor:%s product:0x%04x serial:%d" \
$tileSizeH $tileSizeV \
$tileLocationH $tileLocationV \
$tileCountH $tileCountV \
$vendorid $productid $serialnumber
printf ' enclosure:'
case $enclosuretype in
0) printf 'multiple' ;;
1) printf 'single' ;;
esac
printf ' some:'
case $behaviorsome in
0) printf 'undescribed' ;;
1) printf 'location' ;;
*) printf 'reserved'$behaviorsome ;;
esac
printf ' one:'
case $behavioronly in
0) printf 'undescribed' ;;
1) printf 'location' ;;
2) printf 'scaled' ;;
3) printf 'cloned' ;;
*) printf 'reserved'$behavioronly ;;
esac
if (( tilebezelinformationdescriptoravailable )); then
printf " bezel(t,l,b,r):(%d,%d,%d,%d)x%d" \
$bezelsizetop \
$bezelsizeleft \
$bezelsizebottom \
$bezelsizeright \
$pixelmultiplier
fi
fi
}
flagsstring () {
local label=$1
local bitfirst=$2
local bitlast=$3
local bitdirection=1
local theflags="$4"
local result=""
if [[ -z $theflags ]]; then
result="missing"
else
shift 4
theflags=$((0x$theflags))
(( bitlast < bitfirst )) && bitdirection=-1
result=""
local thebit=$bitfirst
while (( 1 )); do
if (( theflags & (2 ** thebit) )); then
[[ -n $result ]] && result+=","
[[ -z $1 ]] && result+="reserved$thebit" || result+="$1"
fi
(( $# )) && shift
(( thebit == bitlast )) && break
(( thebit = thebit + bitdirection ))
done
fi
[[ -n $result ]] && echo -n " $label:$result"
}
dumpdisplayinterfacefeaturesdata () {
local theblock=$1
local revision=$((0x${theblock:2:2} & 7)) # high 5 bits should be 0
local length=$((0x${theblock:4:2})) # should be 9+N
local colorDepthsRGB=$(flagsstring "RGB" 0 7 ${theblock:6:2} 6 8 10 12 14 16)
local colorDepthsYCbCr444=$(flagsstring "444" 0 7 ${theblock:8:2} 6 8 10 12 14 16)
local colorDepthsYCbCr422=$(flagsstring "422" 0 7 ${theblock:10:2} 8 10 12 14 16)
local colorDepthsYCbCr420=$(flagsstring "420" 0 7 ${theblock:12:2} 8 10 12 14 16)
local minRateYCbCr420string=" 420(MHz):missing"
if [[ -n ${theblock:14:2} ]]; then
local minRateYCbCr420=$((0x${theblock:14:2} * 7425)) # / 100
(( minRateYCbCr420 > 0 )) && minRateYCbCr420string=" 420:≥$(( minRateYCbCr420/100 )).${minRateYCbCr420: -2}MHz" || minRateYCbCr420string=""
fi
local audio=$(flagsstring "audio(kHz)" 7 0 ${theblock:16:2} "32" "44.1" "48")
local colorspace_eotf_combinations1=$(flagsstring "colorspace/eotf#1" 0 7 ${theblock:18:2} \
"sRGB" \
"BT.601" \
"BT.709/BT.1886" \
"Adobe RGB" \
"DCI-P3" \
"BT.2020" \
"BT.2020/SMPTE ST 2084")
local colorspace_eotf_combinations2=$(flagsstring "colorspace/eotf#2" 0 7 ${theblock:20:2})
local additional_colorspace_eotf="missing"
if [[ -n ${theblock:22:2} ]]; then
local num_additional_colorspace_eotf=$((0x${theblock:22:2} & 7))
additional_colorspace_eotf=""
local cendx=""
for (( cendx = 0; cendx < num_additional_colorspace_eotf; cendx++ )); do
(( cendx > 0 )) && additional_colorspace_eotf+=","
local colorspace=""
local eotf=""
if [[ -z ${theblock:24+$cendx*2:2} ]]; then
additional_colorspace_eotf+="missing"
else
case $((0x${theblock:24+$cendx*2:2} >> 4)) in
0) colorspace="Undefined" ;;
1) colorspace="sRGB" ;;
2) colorspace="BT.601" ;;
3) colorspace="BT.709" ;;
4) colorspace="Adobe RGB" ;;
5) colorspace="DCI-P3" ;;
6) colorspace="BT.2020" ;;
7) colorspace="Custom" ;;
*) colorspace="Reserved$((0x${theblock:24+$cendx*2:2} >> 4))" ;;
esac
case $((0x${theblock:24+$cendx*2:2} & 15)) in
0) eotf="Undefined" ;;
1) eotf="sRGB" ;;
2) eotf="BT.601" ;;
3) eotf="BT.1886" ;;
4) eotf="Adobe RGB" ;;
5) eotf="DCI-P3" ;;
6) eotf="BT.2020" ;;
7) eotf="Gamma function" ;;
8) eotf="SMPTE ST 2084" ;;
9) eotf="Hybrid Log" ;;
10) eotf="Custom" ;;
*) eotf="Reserved$((0x${theblock:24+$cendx*2:2} & 15))" ;;
esac
[[ $colorspace = $eotf ]] && additional_colorspace_eotf+="$colorspace" || additional_colorspace_eotf+="$colorspace/$eotf"
fi
done
fi
local lengtherror=""
(( length != 9 + num_additional_colorspace_eotf )) && lengtherror+=" ($length != $((9 + num_additional_colorspace_eotf)))"
[[ -n $additional_colorspace_eotf ]] && additional_colorspace_eotf=" colorspace/eotf#n:$additional_colorspace_eotf"
if (( dodump )); then
printf "revision:%d%s%s%s%s%s%s%s%s%s%s" \
$revision \
$colorDepthsRGB \
$colorDepthsYCbCr444 \
$colorDepthsYCbCr422 \
$colorDepthsYCbCr420 \
$minRateYCbCr420string \
$audio \
$colorspace_eotf_combinations1 \
$colorspace_eotf_combinations2 \
$additional_colorspace_eotf \
$lengtherror
fi
}
processedid () {
(( debug )) && echo ": processedid $@" 1>&2
local ctablocktypetodelete=9999
local displayidblocktypetodelete=9999
local newdisplayidblocks=()
local currentnewdisplayidblock=0
local countAdded=0
while (( $# )); do
local param="$1"; shift
eval "local $param=1"
case "$param" in
doreplacedescriptor)
local doreplacedescriptoroffset="$1"; shift
local newdescriptor="$1"; shift ;;
doadddisplayidblock)
local doadddisplayidblockoffset="$1"; shift
if (( doadddisplayidblockoffset == 0 )); then
doadddisplayidblockoffset=$EndOfLastDisplayIDblock
fi
(( debug )) && echo ": doadddisplayidblock $doadddisplayidblockoffset" 1>&2
while (( $# )); do
(( debug )) && echo ": doadddisplayidblock param: $1" 1>&2
newdisplayidblocks+=("$1"); shift
done
;;
dodeletedisplayidblock)
local deletedisplayidblockoffset="$1"; shift ;;
dodeletedisplayidblockblocktype)
local displayidblocktypetodelete="$1"; shift ;;
dodeletectablocktype)
ctablocktypetodelete="$1"; shift ;;
dosetpreferredisnative)
preferredvalue="$1"; shift ;;
esac
done
# Keep a list of found DisplayID blocks.
DisplayIDblocks=()
# Keep a list of found timings and their offsets.
thetimings=()
thetimingsoffsets=()
thetimingshex=()
HasAudio=0
HasTile=0
# Remember the end of the last DisplayID block (-1 means no DisplayID blocks exist.
# It is also used by adddisplayidblock for inserting new DisplayID blocks at the first available location.
EndOfLastDisplayIDblock=-1
(( debug )) && echo ": parse 1" 1>&2
theproductid=$((0x${theedid:22:2}${theedid:20:2}))
thevendorid=$((0x${theedid:16:4}))
(( debug )) && echo ": parse 2" 1>&2
thevendorcode="$(printf "%06x" $((((thevendorid&0x7c00)<<6)+((thevendorid&0x3e0)<<3)+(thevendorid&0x1f)+0x404040)) | xxd -p -r)"
(( debug )) && echo ": parse 2.1" 1>&2
theweekofmanufacture=$((0x${theedid:32:2}))
(( debug )) && echo ": parse 2.2" 1>&2
theyearofmanufacture=$((0x${theedid:34:2}+1990))
(( debug )) && echo ": parse 3" 1>&2
thevendordir="DisplayVendorID-$(printf "%x" $thevendorid)"
theproductfile="${thevendordir}/DisplayProductID-$(printf "%x" $theproductid)"
themanufacturefile="${thevendordir}/DisplayYearManufacture-$theyearofmanufacture-DisplayWeekManufacture-$theweekofmanufacture"
thefilenamebase="$(printf "EDID_%s_%x_%x" "$thevendorcode" $thevendorid $theproductid)"
(( debug )) && echo ": parse 4" 1>&2
theEDIDversion=$((0x${theedid:0x12*2:2})).$((0x${theedid:0x13*2:2})) # 1.3 or 1.4
isdigital=$(( 0x${theedid:0x14*2:1} > 7 )) # 0 or 1
featuresupportbyte=$((0x${theedid:0x18*2:2}))
featuresupport=$(( (featuresupportbyte >> 3) & 3 ))
preferredisnative=$(( (featuresupportbyte >> 1) & 1 ))
iscontinuous=$(( (featuresupportbyte >> 0) & 1 ))
(( dodump )) && echo "0) EDID $theEDIDversion"
(( dodump )) && printf " Vendor ID: %s %d = 0x%x\n" $thevendorcode $thevendorid $thevendorid
(( dodump )) && printf " Product ID: %d = 0x%x\n" $theproductid $theproductid
if [[ ${theedid:24:8} != 00000000 ]]; then
(( dodump )) && printf " Serial Number: %d" $((0x${theedid:30:2}${theedid:28:2}${theedid:26:2}${theedid:24:2}))
if (( doclearserialnumber )); then
replacebytes 12 00000000
(( dodump )) && printf " changed: unspecified"
fi
(( dodump )) && echo
fi
if [[ ${theedid:32:4} != 0000 ]]; then
if (( dodump )); then
case ${theedid:32:4} in
00*) printf " Made in year $theyearofmanufacture" ;;
ff*) printf " Model year: $theyearofmanufacture" ;;
*) printf " Made in week $theweekofmanufacture of $theyearofmanufacture"
esac
fi
if (( docleardate )); then
replacebytes 16 0000
(( dodump )) && printf " changed: unspecified"
fi
(( dodump )) && echo
fi
(( dodump )) && printf " "
if [[ $theEDIDversion < "1.4" ]]; then
(( dodump )) &&
case $featuresupport in
0) echo "Monochrome or Grayscale" ;;
1) echo "RGB color" ;;
2) echo "Non-RGB color" ;;
3) echo "Undefined" ;;
esac
else
(( dodump )) &&
case $isdigital$featuresupport in
00) echo "Monochrome or Grayscale" ;;
01) echo "RGB color" ;;
02) echo "Non-RGB color" ;;
03) echo "Undefined" ;;
10) echo "RGB 4:4:4" ;;
11) echo "RGB 4:4:4 + YCrCb 4:4:4" ;;
12) echo "RGB 4:4:4 + YCrCb 4:2:2" ;;
13) echo "RGB 4:4:4 + YCrCb 4:4:4 + YCrCb 4:2:2" ;;
esac
if (( do444 )); then
local newfeaturesupportbyte=$(((featuresupportbyte & ~0x18) | (0 << 3) ))
replacebytes 24 $(printf "%02x" $newfeaturesupportbyte)
(( dodump )) &&
case $isdigital in
0) echo ' changed: Monochrome or Grayscale' ;;
1) echo ' changed: RGB 4:4:4' ;;
esac
fi
if (( do422 )); then
local newfeaturesupportbyte=$(((featuresupportbyte & ~0x18) | (3 << 3) ))
replacebytes 24 $(printf "%02x" $newfeaturesupportbyte)
(( dodump )) &&
case $isdigital in
0) echo ' changed: Undefined' ;;
1) echo ' changed: RGB 4:4:4 + YCrCb 4:4:4 + YCrCb 4:2:2' ;;
esac
fi
fi
(( dodump )) &&
case $preferredisnative in
1) echo " Preferred Timing Mode is native pixel format and preferred refresh rate" ;;
esac
if (( dosetpreferredisnative && (preferredvalue != preferredisnative) )); then
local newfeaturesupportbyte=$(((featuresupportbyte & ~2) | (preferredvalue << 1) ))
replacebytes 24 $(printf "%02x" $newfeaturesupportbyte)
(( dodump )) && echo ' changed'
fi
(( dodump )) &&
case $iscontinuous in
0) echo " Display is non-continuous frequency" ;;
1) echo " Display is continuous frequency" ;;
esac
if [[ ${theedid:50:20} != 00000000000000000000 ]]; then
(( dodump )) && printf " Color characteristics: R(%s,%s) G(%s,%s) B(%s,%s) W(%s,%s)" $(colorc 0) $(colorc 1) $(colorc 2) $(colorc 3) $(colorc 4) $(colorc 5) $(colorc 6) $(colorc 7)
if (( doclearchromaticity )); then
replacebytes 25 00000000000000000000
(( dodump )) && printf " changed: unspecified"
fi
(( dodump )) && echo
fi
if [[ ${theedid:70:4} != 0000 ]]; then
(( dodump )) && printf " Established timings: %s" ${theedid:70:4}
if (( doclearestablishedtimings )); then
replacebytes 35 0000
(( dodump )) && printf " changed: unspecified"
fi
(( dodump )) && echo
fi
if [[ ${theedid:74:2} != 00 ]]; then
(( dodump )) && printf " Manufacturer's timings: %s" ${theedid:74:2}
if (( doclearmanufacturerstimings )); then
replacebytes 37 00
(( dodump )) && printf " changed: unspecified"
fi
(( dodump )) && echo
fi
if [[ ${theedid:76:32} != 01010101010101010101010101010101 ]]; then
(( dodump )) && printf " Standard timings: %s" ${theedid:76:32}
if (( doclearstandardtimings )); then
replacebytes 38 01010101010101010101010101010101
(( dodump )) && printf " changed: unspecified"
fi
(( dodump )) && echo
fi
detailed_blocks 54 108 "$dodump" "$doreplacedescriptoroffset" "$newdescriptor"
local blockoffset=128
while (( blockoffset*2 < ${#theedid} )); do
(( debug )) && echo ": extension block $blockoffset" 1>&2
local theblock=${theedid:$blockoffset*2:254}
(( debug )) && echo ": extension theblock = $theblock" 1>&2
local blocktag=${theblock:0:2}
(( debug )) && echo ": blocktag = $blocktag" 1>&2
if [[ $blocktag = 02 ]]; then
CTAversion=$((0x${theblock:2:2}))
(( debug )) && echo ": CTAversion = $CTAversion" 1>&2
detailedTimingDescriptorsOffset=$((0x${theblock:4:2}))
if (( CTAversion > 1 )); then
(( dodump )) && echo "$blockoffset) CTA-861 extension block with new version $CTAversion"
YCbCrSupportbyte=$((0x${theblock:6:2}))
YCbCrSupport=$((( YCbCrSupportbyte >> 4) & 3))
(( dodump )) &&
case $YCbCrSupport in
0) echo " No YCbCr support" ;;
1) echo " YCbCr 4:2:2" ;;
2) echo " YCbCr 4:4:4" ;;
3) echo " YCbCr 4:4:4, YCbCr 4:2:2" ;;
esac
if (( do444 )); then
newYCbCrSupportbyte=$(((YCbCrSupportbyte & ~0x30) | (0 << 4) ))
replacebytes $((blockoffset+3)) $(printf "%02x" $newYCbCrSupportbyte)
(( dodump )) && echo ' changed: No YCbCr support'
fi
if (( do422 )); then
newYCbCrSupportbyte=$(((YCbCrSupportbyte & ~0x30) | (3 << 4) ))
replacebytes $((blockoffset+3)) $(printf "%02x" $newYCbCrSupportbyte)
(( dodump )) && echo ' changed: YCbCr 4:4:4, YCbCr 4:2:2'
fi
BasicAudioSupport=$((( YCbCrSupportbyte >> 6) & 1))
(( dodump )) &&
case $BasicAudioSupport in
1) echo " Basic audio support" ;;
esac
if (( CTAversion > 2 )); then
CTAdatablockoffset=4
(( dodump && (CTAdatablockoffset < detailedTimingDescriptorsOffset) )) && echo " CTA data blocks:"
while (( CTAdatablockoffset < detailedTimingDescriptorsOffset )); do
CTAdatablocklength=$(((0x${theblock:$CTAdatablockoffset*2:2} & 0x1f) + 1))
CTAdatablock=${theblock:$CTAdatablockoffset*2:$CTAdatablocklength * 2}
CTAtagcode=$((0x${CTAdatablock:0:2} >> 5))
if (( CTAtagcode == 7 )); then
CTAtagcode=$((0x${CTAdatablock:2:2} + 700))
fi
if (( dodump )); then
echo -n " $((blockoffset + CTAdatablockoffset))) "
case $CTAtagcode in
0) printf "Reserved" ;;
1) printf "Audio" ;;
2) printf "Video" ;;
3) printf "Vendor-specific" ;;
4) printf "Speaker allocation" ;;
5) printf "Display transfer characteristic" ;;
6) printf "Reserved" ;;
700) printf "Video capability" ;;
701) printf "Vendor-specific video" ;;
702) printf "VESA display device" ;;
703) printf "VESA video timing block" ;;
704) printf "Reserved for HDMI video" ;;
705) printf "Colorimetry" ;;
706) printf "HDR static metadata" ;;
707) printf "HDR dynamic metadata" ;;
713) printf "Video format preference" ;;
714) printf "YCbCr 4:2:0 video" ;;
715) printf "YCbCr 4:2:0 capability map" ;;
716) printf "Reserved for CTA miscellaneous audio fields" ;;
717) printf "Vendor-specific audio" ;;
718) printf "Reserved for HDMI audio" ;;
719) printf "Room configuration" ;;
720) printf "Speaker location" ;;
732) printf "InfoFrames" ;;
*)
if (( CTAtagcode <= 712 )); then
printf "Reserved for video-related"
elif (( CTAtagcode <= 731 )); then
printf "Reserved for audio-related"
else
printf "Reserved"
fi
printf " (e${CTAtagcode: -2})"
;;
esac
echo -n ": $CTAdatablock"
fi
case $CTAtagcode in
1)
(( dodump )) && echo
HasAudio=1
;;
3)
IEEEOUI=${CTAdatablock:6:2}${CTAdatablock:4:2}${CTAdatablock:2:2}
(( dodump )) && echo -n " = $IEEEOUI:"
case $IEEEOUI in
000c03)
(( dodump )) && echo " HDMI Licensing, LLC -> H14b VSDB"
H14bByte=${CTAdatablock:12:2}
if [[ -n $H14bByte ]]; then
H14bByte=$((0x${H14bByte}))
H14b=$((( H14bByte >> 3) & 1))
(( dodump )) &&
case $H14b in
0) echo " Support YCbCr 4:4:4 - No" ;;
1) echo " Support YCbCr 4:4:4 - Yes" ;;
esac
if (( do444 )); then
newH14bByte=$(((H14bByte & ~0x08) | (0 << 3) )) # 0: No (RGB only), 1: Yes (YCbCr 4:4:4)
replacebytes $((blockoffset+CTAdatablockoffset+6)) $(printf "%02x" $newH14bByte)
(( dodump )) && echo " changed: Support YCbCr 4:4:4 - No"
fi
if (( do422 )); then
newH14bByte=$(((H14bByte & ~0x08) | (1 << 3) )) # 0: No (RGB only), 1: Yes (YCbCr 4:4:4)
replacebytes $((blockoffset+CTAdatablockoffset+6)) $(printf "%02x" $newH14bByte)
(( dodump )) && echo " changed: Support YCbCr 4:4:4 - Yes"
fi
fi
;;
c45dd8)
(( dodump )) && echo " HDMI Forum -> HF-VSDB"
HFByte=$((0x${CTAdatablock:14:2}))
HF=$((( HFByte >> 0) & 7))
(( dodump )) &&
case $HF in
0) echo " 4:2:0 10/12/16 bpc - No" ;;
1) echo " 4:2:0 10 bpc - Yes" ;;
2) echo " 4:2:0 12 bpc - Yes" ;;
3) echo " 4:2:0 10/12 bpc - Yes" ;;
4) echo " 4:2:0 16 bpc - Yes" ;;
5) echo " 4:2:0 10/16 bpc - Yes" ;;
6) echo " 4:2:0 12/16 bpc - Yes" ;;
7) echo " 4:2:0 10/12/16 bpc - Yes" ;;
esac
if (( do444 )); then
newHFByte=$(((HFByte & ~0x07) | (0 << 0) ))
replacebytes $((blockoffset+CTAdatablockoffset+7)) $(printf "%02x" $newHFByte)
(( dodump )) && echo " changed: 4:2:0 10/12/16 bpc - No"
fi
if (( do422 )); then
newHFByte=$(((HFByte & ~0x07) | (7 << 0) ))
replacebytes $((blockoffset+CTAdatablockoffset+7)) $(printf "%02x" $newHFByte)
(( dodump )) && echo " changed: 4:2:0 10/12/16 bpc - Yes"
fi
;;
*)
(( dodump )) && echo " Unknown OUI"
;;
esac
;;
*)
(( dodump )) && echo
;;
esac
if (( CTAtagcode == ctablocktypetodelete )); then
((detailedTimingDescriptorsOffset-=CTAdatablocklength))
replacebytes $((blockoffset+2)) $(printf "%02x" $detailedTimingDescriptorsOffset)
local nextctablockoffset=$((CTAdatablockoffset+CTAdatablocklength))
replacebytes $((blockoffset+CTAdatablockoffset)) ${theedid:(blockoffset+nextctablockoffset)*2:(127-nextctablockoffset)*2}${CTAdatablock//?/0}
theblock=${theedid:$blockoffset*2:254}
CTAdatablocklength=0
(( dodump )) && echo " changed: deleted"
fi
((CTAdatablockoffset+=CTAdatablocklength))
done
fi
else
(( dodump )) && echo "$blockoffset) CTA-861 extension block with old version $CTAversion"
fi
detailed_blocks $((blockoffset+detailedTimingDescriptorsOffset)) $((blockoffset + 127 - 18)) "$dodump" "$doreplacedescriptoroffset" "$newdescriptor"
elif [[ $blocktag = 70 ]]; then
(( debug )) && echo ": parsing DisplayID extension block" 1>&2
# 00 edid tag 70
#
# 01 1 DisplayID version
# 02 2 DisplayID len 0x79
# 03 3 DisplayID product type
# 04 4 DisplayID nextcount
#
# 05 00 DisplayID block #1
# 06 01 DisplayID block #1 revision
# 07 02 DisplayID block #1 length
#
# 7E 5 displayID checksum
#
# 7F EDID checksum
DisplayIDversion=${theblock:2:1}.${theblock:3:1}
# maximum length of DisplayID section in an EDID extension block is 121 = 0x79 bytes.
# this is the length of all the DisplayID blocks
DisplayIDlength=$((0x${theblock:4:2}))
DisplayIDproducttype=$((0x${theblock:6:2}))
DisplayIDextcount=$((0x${theblock:8:2}))
DisplayIDblockoffset=5
if (( doadddisplayidblockoffset == -1 )); then
doadddisplayidblockoffset=$((blockoffset + DisplayIDblockoffset))
fi
local newdisplayidblock=""
local newDisplayIDblocklength=0
local lastDisplayIDblockoffset=0
while (( DisplayIDblockoffset < DisplayIDlength + 5 )); do
if (( blockoffset + DisplayIDblockoffset == doadddisplayidblockoffset )); then
(( debug )) && echo ": found $doadddisplayidblockoffset" 1>&2
# concatenate all the new blocks that will fit
while (( currentnewdisplayidblock < ${#newdisplayidblocks[@]} )); do
local onedisplayidblock=${newdisplayidblocks[currentnewdisplayidblock+arrstart]}
(( debug )) && echo ": testing $onedisplayidblock" 1>&2
if (( ${#onedisplayidblock}/2 > 0x79 )); then
echo "Error: DisplayID block is too large: ${onedisplayidblock}" 1>&2
else
if (( (${#newdisplayidblock} + ${#onedisplayidblock})/2 > 0x7e - DisplayIDblockoffset )); then
(( debug )) && echo ": overflow" 1>&2
break
fi
newdisplayidblock+="${onedisplayidblock}"
((countAdded++))
(( debug )) && echo ": $countAdded: concetenate result: $newdisplayidblock" 1>&2
fi
((currentnewdisplayidblock++))
done
newdisplayidblocks=("${newdisplayidblocks[@]:$currentnewdisplayidblock}")
currentnewdisplayidblock=0
newDisplayIDblocklength=$((${#newdisplayidblock}/2))
if (( countAdded == 0 )); then
# none fit here, try in the next block
doadddisplayidblockoffset=-1
fi
fi
# stop when the remaining bytes are 0
local DisplayIDremains=${theblock:$DisplayIDblockoffset * 2:($DisplayIDlength + 5 - $DisplayIDblockoffset)*2}
if [ -z ${DisplayIDremains//0/} ]; then
break
fi
DisplayIDblocklength=$((0x${theblock:$DisplayIDblockoffset * 2 + 4:2} + 3))
# blocks that won't fit are inserted into a list that will be added later
if (( DisplayIDblockoffset + DisplayIDblocklength + newDisplayIDblocklength > 0x7e )); then
DisplayIDblock=${theblock:$DisplayIDblockoffset*2:$DisplayIDblocklength * 2}
newdisplayidblocks+=("$DisplayIDblock")
if (( lastDisplayIDblockoffset == 0)); then
lastDisplayIDblockoffset=$DisplayIDblockoffset
fi
fi
((DisplayIDblockoffset+=DisplayIDblocklength))
done
if (( lastDisplayIDblockoffset == 0)); then
lastDisplayIDblockoffset=$DisplayIDblockoffset
fi
(( dodump )) && printf "%s) DisplayID extension block: version %s, type %d\n" $blockoffset $DisplayIDversion $DisplayIDproducttype
DisplayIDblockoffset=5
while (( DisplayIDblockoffset < DisplayIDlength + 5 )); do
if (( blockoffset + DisplayIDblockoffset == doadddisplayidblockoffset )); then
replacebytes $((doadddisplayidblockoffset)) "$newdisplayidblock${theblock:$DisplayIDblockoffset*2:($lastDisplayIDblockoffset-$DisplayIDblockoffset)*2}"
doadddisplayidblockoffset=-1
((lastDisplayIDblockoffset+=newDisplayIDblocklength))
if (( DisplayIDlength + 5 < lastDisplayIDblockoffset )); then
DisplayIDlength=$((lastDisplayIDblockoffset - 5))
replacebytes $((blockoffset+2)) $(printf "%02x" $DisplayIDlength)
fi
theblock=${theedid:$blockoffset*2:254}
fi
DisplayIDblocklength=$((0x${theblock:$DisplayIDblockoffset * 2 + 4:2} + 3))
(( dodump )) && echo -n " $((blockoffset + DisplayIDblockoffset)))"
DisplayIDblock=${theblock:$DisplayIDblockoffset*2:$DisplayIDblocklength * 2}
DisplayIDtagcode=${DisplayIDblock:0:2}
# stop when the remaining bytes are 0
local DisplayIDremains=${theblock:$DisplayIDblockoffset * 2:($DisplayIDlength + 5 - $DisplayIDblockoffset)*2}
if [ -z ${DisplayIDremains//0/} ]; then
(( dodump )) && echo
break
fi
if (( dodump )); then
printf " "
case $DisplayIDtagcode in
# DisplayID 1.3
00) printf "Product identification" ;;
01) printf "Display parameters" ;;
02) printf "Color characteristics" ;;
03) printf "Type 1 detailed timing" ;;
04) printf "Type 2 detailed timing" ;;
05) printf "Type 3 short timing" ;;
06) printf "Type 4 DMT timing" ;;
07) printf "VESA timings" ;;
08) printf "CTA timings" ;;
09) printf "Video timing range" ;;
0a) printf "Product serial number" ;;
0b) printf "General purpose ASCII string" ;;
0c) printf "Display device data" ;;
0d) printf "Interface power sequencing" ;;
0e) printf "Transfer characterisitics" ;;
0f) printf "Display interface" ;;
10) printf "Stereo display interface" ;;
12) printf "Tiled display topology" ;;
# DisplayID 2.0
20) printf "Product ID data" ;;
21) printf "Display parameters data" ;;
22) printf "Type 7 timing - detailed timing data" ;;
23) printf "Type 8 timing - enumerated timing code data" ;;
24) printf "Type 9 timing - formula-based timing data" ;;
25) printf "Dynamic video timing range limits data" ;;
26) printf "Display interface features data" ;;
27) printf "Stereo display interface data" ;;
28) printf "Tiled display topology data" ;;
29) printf "ContainerID data" ;;
# 2Ah .. 7Dh RESERVED for Additional VESA-defined Data Blocks
7e) printf "2.0 Vendor-specific data" ;;
7f) printf "1.3 Vendor-specific data" ;;
81) printf "CTA DisplayID data" ;;
# 82h .. FFh RESERVED
*)
if (( 0x$DisplayIDtagcode <= 0x1f )); then
printf "Reserved for legacy"
elif (( 0x$DisplayIDtagcode <= 0x7d )); then
printf "Reserved for VESA"
elif (( 0x$DisplayIDtagcode <= 7f )); then
printf "Reserved"
else
printf "Reserved for external standards"
fi
printf " (${DisplayIDtagcode})"
;;
esac
echo -n ": $DisplayIDblock"
fi
local willberemoved=$(( (blockoffset + DisplayIDblockoffset == deletedisplayidblockoffset) || (0x$DisplayIDtagcode == displayidblocktypetodelete) ))
case $DisplayIDtagcode in
03)
local timing1offset=3
(( dodump == 1 && DisplayIDblocklength > 23 )) && echo
while ((timing1offset < DisplayIDblocklength )); do
local type1timing=${DisplayIDblock:$timing1offset*2:40}
if (( dodump )); then
(( DisplayIDblocklength > 23 )) && echo -n " $((blockoffset+DisplayIDblockoffset+timing1offset))) $type1timing"
fi
local type1timingtext="$(dumptype1timingdescriptor $type1timing)"
if (( willberemoved == 0 )); then
thetimings+=("$type1timingtext")
thetimingsoffsets+=("$(( blockoffset + DisplayIDblockoffset + timing1offset ))")
thetimingshex+=("$type1timing")
fi
(( dodump )) && echo " = $type1timingtext"
((timing1offset+=20))
done
;;
12)
HasTile=1
(( dodump )) && printf " = "
dumptileddisplaytopologyblock $DisplayIDblock
(( dodump )) && echo
;;
26) (( dodump )) && printf " = "
dumpdisplayinterfacefeaturesdata $DisplayIDblock
(( dodump )) && echo
;;
*)
(( dodump )) && echo
;;
esac
if (( willberemoved )); then
(( debug )) && echo ":" replacebytes $((blockoffset + DisplayIDblockoffset)) "${theblock:($DisplayIDblockoffset + $DisplayIDblocklength)*2:($lastDisplayIDblockoffset - $DisplayIDblockoffset - $DisplayIDblocklength)*2}${DisplayIDblock//?/0}" 1>&2
(( debug )) && echo ":" $DisplayIDblockoffset $DisplayIDblocklength $lastDisplayIDblockoffset ${DisplayIDblock//?/0} 1>&2
replacebytes $((blockoffset + DisplayIDblockoffset)) "${theblock:($DisplayIDblockoffset + $DisplayIDblocklength)*2:($lastDisplayIDblockoffset - $DisplayIDblockoffset - $DisplayIDblocklength)*2}${DisplayIDblock//?/0}"
((lastDisplayIDblockoffset-=DisplayIDblocklength))
theblock=${theedid:$blockoffset*2:254}
(( dodump )) && echo " changed: deleted"
deletedisplayidblockoffset=0
else
DisplayIDblocks+=("$DisplayIDblock")
(( dodump && (countAdded > 0) )) && echo " changed: added $countAdded"
((DisplayIDblockoffset+=DisplayIDblocklength))
EndOfLastDisplayIDblock=$((blockoffset + DisplayIDblockoffset))
fi
done
else
(( dodump )) && echo "$blockoffset) Extension block: type:$blocktag: $theblock"
fi
((blockoffset+=128))
if (( (blockoffset*2 == ${#theedid}) && (doadddisplayidblockoffset == -1) && (${#newdisplayidblocks[@]} > 0) )); then
adddisplayidextensionblock ${DisplayIDversion:=1.3} ${DisplayIDproducttype:=0}
fi
done
(( dodump )) && echo "$blockoffset) End"
# post processing
local timingndx=0
local tileTimingsCurrent=""
for ((timingndx = 0 ; timingndx < ${#thetimings[@]} ; timingndx++)); do
local thetiming="${thetimings[timingndx+arrstart]}"
local thewidth=${thetiming%%x*}
local theheight=${thetiming#*x}
theheight=${theheight%%@*}
local thetimingrefresh=${thetiming#*@}
thetimingrefresh=${thetimingrefresh%%Hz*}
thetimingrefresh=0000${thetimingrefresh/./}
thetimingrefresh=${thetimingrefresh: -7}
if (( thewidth > maxresH )); then
maxresH=$thewidth
maxresV=$theheight
fi
if (( HasTile )); then
if [[ "$thetiming" =~ "${tileSizeH}x${tileSizeV}@([0-9.]+)Hz.*" ]]; then
tileTimingsCurrent+="$thetimingrefresh $thetiming\n"
fi
fi
done
if (( HasTile )); then
if [[ -n $tileTimingsCurrent ]]; then
tileTimings=$(echo "$tileTimingsCurrent" | sort -r)
tileRefresh=$(((10#${tileTimings%% *} + 50)/100)) # round to 1 decimal
if (( tileRefresh % 10 )); then
tileRefresh=$((tileRefresh / 10)).${tileRefresh: -1}
else
tileRefresh=$((tileRefresh / 10))
fi
fi
fi
}
createdetailedtiming () {
# currently supports only "Normal Display" with "Digital Separate Sync"
for param in MHz hactive hfront hpulse hback vactive vfront vpulse vback hsync vsync hmm vmm hborder vborder; do
eval "local $param=\"$1\""; (( $# )) && shift
done
local interlaced=0
[[ -n ${vactive//[^i]} ]] && interlaced=1
vactive=${vactive//[^0-9]}
[[ "$hsync" = '+' ]] && hsync=1 || hsync=0
[[ "$vsync" = '+' ]] && vsync=1 || vsync=0
local MHzint=$(((10#$(printf "0${MHz}.000" | sed -E "s/^([0-9]*)\.([0-9])\.*([0-9])\.*([0-9]).*/\1\2\3\4/g") + 5) / 10))
local hblank=$((hfront + hpulse + hback))
local vblank=$((vfront + vpulse + vback))
printf "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" \
$((MHzint & 255)) $((MHzint >> 8)) \
$((hactive & 255)) \
$((hblank & 255)) \
$(((hactive >> 8 << 4) + (hblank >> 8))) \
$((vactive & 255)) \
$((vblank & 255)) \
$(((vactive >> 8 << 4) + (vblank >> 8))) \
$((hfront & 255)) \
$((hpulse & 255)) \
$((((vfront & 15) << 4) + (vpulse & 15))) \
$(((hfront >> 8 << 6) + (hpulse >> 8 << 4) + (vfront >> 4 << 2) + (vpulse >> 4))) \
$((${hmm:=0} & 255)) \
$((${vmm:=0} & 255)) \
$(((${hmm:=0} >> 8 << 4) + (${vmm:=0} >> 8))) \
${hborder:=0} \
${vborder:=0} \
$(((interlaced << 7) + 0x18 + (vsync << 2) + (hsync << 2)))
}
createtype1timingdescriptor () {
# currently supports only "Normal Display" with "Digital Separate Sync"
# no 3D, no interlaced
for param in MHz hactive hfront hpulse hback vactive vfront vpulse vback hsync vsync preferred; do
eval "local $param=\"$1\""; (( $# )) && shift
done
local interlaced=0
[[ -n ${vactive//[^i]} ]] && interlaced=1
vactive=${vactive//[^0-9]}
[[ "$hsync" = '+' ]] && hsync=1 || hsync=0
[[ "$vsync" = '+' ]] && vsync=1 || vsync=0
local MHzint=$(((10#$(printf "0${MHz}.000" | sed -E "s/^([0-9]*)\.([0-9])\.*([0-9])\.*([0-9]).*/\1\2\3\4/g") + 5) / 10))
local hblank=$((hfront + hpulse + hback))
local vblank=$((vfront + vpulse + vback))
local aspect=8
case $(((hactive * 10000 / vactive + 5)/10)) in
1000) aspect=0 ;;
1250) aspect=1 ;;
1333) aspect=2 ;;
1667) aspect=3 ;;
1778) aspect=4 ;;
1600) aspect=5 ;;
2370) aspect=6 ;;
1896) aspect=7 ;;
esac
((MHzint-=1))
((hactive-=1))
((hblank-=1))
((hfront-=1))
((hpulse-=1))
((vactive-=1))
((vblank-=1))
((vfront-=1))
((vpulse-=1))
printf "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" \
$((MHzint & 255)) $(((MHzint >> 8) & 255)) $((MHzint >> 16)) \
$(((${preferred:=0} << 7) + (interlaced << 4) + aspect)) \
$((hactive & 255)) $((hactive >> 8)) \
$((hblank & 255)) $((hblank >> 8)) \
$((hfront & 255)) $((((hfront >> 8) & 127) + (hsync << 7))) \
$((hpulse & 255)) $((hpulse >> 8)) \
$((vactive & 255)) $((vactive >> 8)) \
$((vblank & 255)) $((vblank >> 8)) \
$((vfront & 255)) $((((vfront >> 8) & 127) + (vsync << 7))) \
$((vpulse & 255)) $((vpulse >> 8))
}
createtype1timingblock () {
local type1timingblock=""
while (( $# )); do
type1timingblock+=$1
shift
done
printf "0300%02x%s" $((${#type1timingblock}/2)) "$type1timingblock"
}
addchromasubsampling () {
processedid do422 dodeletectablocktype 715 # = YCbCr 4:2:0 capability map
repairchecksums
}
removechromasubsampling () {
processedid do444 dodeletectablocktype 715 # = YCbCr 4:2:0 capability map
repairchecksums
}
deletectablocktype () {
processedid dodeletectablocktype "$1"
}
replacedescriptor () {
processedid doreplacedescriptor "$1" "$2"
repairchecksums
}
cleardate () {
processedid docleardate
repairchecksums
}
clearserialnumber () {
processedid doclearserialnumber
repairchecksums
}
setpreferredisnative () {
processedid dosetpreferredisnative "$1"
repairchecksums
}
clearchromaticity () {
processedid doclearchromaticity
repairchecksums
}
clearestablishedtimings () {
processedid doclearestablishedtimings
repairchecksums
}
clearmanufacturerstimings () {
processedid doclearmanufacturerstimings
repairchecksums
}
clearstandardtimings () {
processedid doclearstandardtimings
repairchecksums
}
deletedisplayidblock () {
processedid dodeletedisplayidblock "$1"
repairchecksums
}
deletedisplayidblocktype () {
processedid dodeletedisplayidblockblocktype "$1"
repairchecksums
}
adddisplayidblock () {
processedid doadddisplayidblock "$@"
repairchecksums
}
dumpedid () {
processedid dodump
}
dumpedidall () {
local saveedid="$theedid"
local thefolderpath="$1"
local i=""
for ((i = 1 ; i <= ${#edids[@]} ; i++)); do
useedidnum $i
dumpedid > "${thefolderpath:=.}/${thefilenamebase}_dumpedid.txt"
done
useedidstring "$saveedid"
}
createdummydescriptor () {
echo -n "000000100000000000000000000000000000"
}
createemptydescriptor () {
echo -n "000000000000000000000000000000000000"
}
#=========================================================================================
# Use EDID
clearedidinfo () {
tileTimings=""
tileSizeH=""
tileSizeV=""
tileRefresh=""
maxresH=0
maxresV=0
}
useedidstring () {
clearedidinfo
(( debug )) && echo ": useedidstring $1" 1>&2
theedid="$1"
processedid
}
useedidnum () {
clearedidinfo
theedid=${edids[arrstart - 1 + $1]}
processedid
thefilenamebase="${thefilenamebase}_$i"
}
#=========================================================================================
# Get EDID
clearedids () {
edids=()
paths=()
}
clearedids
applypatches () {
thefilename="$1"
sourcename="$2"
if [[ -f "${thefilename}" ]]; then
if /usr/libexec/PlistBuddy -c 'Print :edid-patches' "${thefilename}" > /dev/null 2>&1; then
local index=0
while [ -n ""$(/usr/libexec/PlistBuddy -c "Print :edid-patches:$index:offset" "${thefilename}" 2> /dev/null) ]; do
local theoffset=$(/usr/libexec/PlistBuddy -c "Print :edid-patches:$index:offset" "${thefilename}")
local thedata=$(/usr/libexec/PlistBuddy -c "Print :edid-patches:$index:data" "${thefilename}" | xxd -p -c 99999)
thedata=${thedata%0a} # remove extra linefeed that was added by PlistBuddy
theedid=${theedid:0:$theoffset*2}${thedata}${theedid:$theoffset*2 + ${#thedata}}
((index+=1))
done
if (( ${#theedid} % 256 == 0 )); then
if [[ -n $sourcename ]]; then
addedid "${thefilename}:edid-patches of ($sourcename)" "$theedid"
else
addedid "${thefilename}:edid-patches" "$theedid"
fi
else
theedid=""
fi
fi
fi
}
addedid () {
local saveedid="$theedid"
local thepath="$1"
useedidstring "$2"
local isnew=1
local i=""
for ((i = 0 ; i < ${#edids[@]} ; i++)); do
if [[ $theedid = ${edids[i+arrstart]} ]]; then
if [[ ! "$(printf "_\n%s\n_" "${paths[i+arrstart]}")" =~ $(printf ".*\n%s\n.*" "${thepath}") ]]; then
paths[i+arrstart]="$(printf "%s\n%s" "${paths[i+arrstart]}" "$thepath")"
fi
isnew=0
break
fi
done
if (( isnew )); then
(( debug )) && echo ": isnew" 1>&2
edids+=("$theedid")
paths+=("$thepath")
local newndx=${#edids[@]}
for thedir in "/System" ""; do
local fulldir="$thedir/Library/Displays/Contents/Resources/Overrides"
for thefilename in "$fulldir/$themanufacturefile" "$fulldir/$theproductfile"; do
(( debug )) && echo ": checking $thefilename" 1>&2
if [[ -f "${thefilename}" ]]; then
loadoverridefile "${thefilename}"
[[ -n $lastoverrideedid ]] && theedid=$lastoverrideedid
applypatches "${thefilename}" "$newndx"
fi
done
for thefilename in "$fulldir/${themanufacturefile}.mtdd" "$fulldir/${theproductfile}.mtdd"; do
(( debug )) && echo ": checking $thefilename" 1>&2
if [[ -f "${thefilename}" ]]; then
loadmtddfile "${thefilename}"
fi
done
done
fi
useedidstring "$saveedid"
}
loadoverridefile () {
while (( $# )); do
local thefilename="$1"
shift
# lastoverrideedid is not local
lastoverrideedid=$(/usr/libexec/PlistBuddy -c 'Print :IODisplayEDID' "${thefilename}" 2> /dev/null | xxd -p -c 99999)
lastoverrideedid=${lastoverrideedid%0a} # remove extra linefeed that was added by PlistBuddy
[[ -n $lastoverrideedid ]] && addedid "${thefilename}:IODisplayEDID" "$lastoverrideedid"
done
}
loadmtddfile () {
while (( $# )); do
local thefilename="$1"
shift
local theedid=$(/usr/libexec/PlistBuddy -c 'Print :display:overlay' "${thefilename}" 2> /dev/null | xxd -p -c 99999)
theedid=${theedid%0a} # remove extra linefeed that was added by PlistBuddy
[[ -n $theedid ]] && addedid "${thefilename}:overlay" "$theedid"
done
}
loadswitchresxfile () {
while (( $# )); do
local thefilename="$1"
shift
local theedid=$(sed -n -E -e "/^ *< ([0-9A-F ]+) >/s//\1/p" "${thefilename}" | xxd -r -p | xxd -p -c 99999)
addedid "${thefilename}:switchresx" "$theedid"
done
}
loadmamhexfile () {
while (( $# )); do
local thefilename="$1"
shift
local theedid=$(sed -n -E -e "/^:[0-9A-F]{8}([0-9A-F]{64})[0-9A-F]{2}.?$/s//\1/p" "${thefilename}" | xxd -p -r | xxd -p -c 99999)
addedid "${thefilename}:Monitor Asset Manager.hex" "$theedid"
done
}
loadmaminffile () {
while (( $# )); do
local thefilename="$1"
shift
local theedid=$(sed -n -E -e '/^HKR,EDID_OVERRIDE,".+",0x01((,0x[0-9A-F]{2})*).?$/s//\1/;s/,0x//gp' "${thefilename}" | xxd -p -r | xxd -p -c 99999)
addedid "${thefilename}:Monitor Asset Manager.hex" "$theedid"
done
}
loadmamdatfile () {
while (( $# )); do
local thefilename="$1"
shift
local theedid=$(sed -n -E -e '/^[0-9A-F]{2} \|(( [0-9A-F]{2})*).?$/s//\1/p' "${thefilename}" | xxd -r -p | xxd -p -c 99999)
addedid "${thefilename}:Monitor Asset Manager.dat" "$theedid"
done
}
numstrings=0
loadstring () {
local sourcename="$2"
if [[ -z $sourcename ]]; then
((numstrings++))
sourcename="string:$numstrings"
fi
addedid "$sourcename" "$1"
}
loadbinfile () {
while (( $# )); do
local thefilename="$1"
shift
addedid "$thefilename" ""$(xxd -p -c 99999 < "$thefilename")
done
}
loadhexfile () {
while (( $# )); do
local thefilename="$1"
shift
addedid "$thefilename" ""$(xxd -r < "$thefilename" | xxd -p -c 99999)
done
}
loadoneediditem () {
local ediditem="$1"
local thepath="${ediditem% = <*}"
local theedid="${ediditem##* = <}"
local theedid="${theedid%>}"
(( debug )) && echo ":" addedid "${thepath}" "${theedid}" 1>&2
addedid "${thepath}" "${theedid}"
}
loadagdcfile () {
while (( $# )); do
local thefilename="$1"
shift
IFS=$'\n'
for ediditem in $(
perl -e '
$thepath=""; while (<>) {
if ( m|^(?:\[\d+\] )?IOService:(/.*)| ) { $thepath = $1 }
if ( /^[#|]* ?EDID Dump (Port \d+) - Start( ##)?/ ) { $theport = $1; $edid = "" }
if ( m|^( )?/\*? ...: \*?/ ?(.*)| ) { $edid .= $2 }
if ( /^(## )?EDID Dump (Port \d+) - End( ##)?/ and length $edid > 0 ) { $edid =~ s/0x(..)(, *)?/$1/g; print "'"${thefilename}"':" . $thepath . "/" . $theport . " = <" . $edid . ">\n" }
}
' < "${thefilename}"
); do
(( debug )) && echo ":" loadoneediditem "${ediditem}" 1>&2
loadoneediditem "${ediditem}"
done
done
}
loadmamfile () {
while (( $# )); do
local thefilename="$1"
shift
IFS=$'\n'
for ediditem in $(
perl -e '
$thepath=""; while (<>) {
if ( m|^(Monitor( #.*\S)?)\s*$| ) { $thepath = $1 }
if ( /^Raw data.?$/ ) { $edid = "" }
if ( m| *(([0-9A-F]{2},){31}[0-9A-F]{2},).?$| ) { $edid .= $1 }
if ( m| *(([0-9A-F]{2},){31}[0-9A-F]{2})[^,]*$| ) { $edid .= $1 . ","; $edid =~ s/(..),/$1/g; print "'"${thefilename}"':" . $thepath . " = <" . lc $edid . ">\n" }
}
' < "${thefilename}"
); do
(( debug )) && echo ":" loadoneediditem "${ediditem}" 1>&2
loadoneediditem "${ediditem}"
done
done
}
loadagdc () {
local thefilename="$1"
if [[ -z $thefilename ]]; then
thefilename=`mktemp /tmp/local_AGDCDiagnose.XXXXXX` || exit 1
fi
/System/Library/Extensions/AppleGraphicsControl.kext/Contents/MacOS/AGDCDiagnose -a > "$thefilename" 2>&1
loadagdcfile "$thefilename"
}
loadioregfile () {
while (( $# )); do
local thefilename="$1"
shift
IFS=$'\n'
for ediditem in $(
perl -e '
$thepath=""; while (<>) {
if ( /^([ |]*)\+\-o (.+) </ ) { $indent = (length $1) / 2; $name = $2; $thepath =~ s|^((/[^/]*){$indent}).*|$1/$name| }
if ( /^[ |]*"([^"]+)" = <(00ffffffffffff00.*)>/i ) { print "'"${thefilename}"'" . ":" . $thepath . "/" . $1 . " = <" . $2 . ">\n" }
}
' < "${thefilename}"
); do
loadoneediditem "${ediditem}"
done
done
}
loadioreg () {
local tmpfilename=`mktemp /tmp/local_ioreg.XXXXXX` || exit 1
ioreg -lw0 > "$tmpfilename"
loadioregfile "$tmpfilename"
}
listedids () {
local saveedid="$theedid"
local i=""
for ((i = 1 ; i <= ${#edids[@]} ; i++)); do
useedidnum $i
echo "$i)"
echo "vendor:$thevendorid ($thevendorcode) product:$theproductid"
echo "override product name:$theproductfile"
echo "override date name:$themanufacturefile"
echo "strings:"$(echo -n "$theedid" | xxd -p -r | strings - )
echo "theedid=$theedid"
echo "sources:"
echo "${paths[i+arrstart-1]}"
echo
done
useedidstring "$saveedid"
}
#=========================================================================================
# Files from EDID
edidbin () {
echo -n "$theedid" | xxd -p -r
}
edidbinall () {
local saveedid="$theedid"
local thefolderpath="$1"
local i=""
for ((i = 1 ; i <= ${#edids[@]} ; i++)); do
useedidnum $i
edidbin > "${thefolderpath:=.}/${thefilenamebase}.bin"
done
useedidstring "$saveedid"
}
decode () {
local tmpfilename=`mktemp /tmp/edidbin.XXXXXX` || exit 1
edidbin > "$tmpfilename"
edid-decode -C --skip-sha "$tmpfilename"
}
decodeall () {
local saveedid="$theedid"
local thefolderpath="$1"
local i=""
for ((i = 1 ; i <= ${#edids[@]} ; i++)); do
useedidnum $i
decode > "${thefolderpath:=.}/${thefilenamebase}_edid-decode.txt"
done
useedidstring "$saveedid"
}
agdcdevicedump () {
# Indent the Device Dump section of a AGDCDiagnose file.
# Note that some DICT sizes seem to be greater than the number of lines included in the dump.
local thefilename="$1"
perl -e '
sub dict {
my $indent = $_[0];
my $lines = $_[1];
while ($lines-- != 0) {
my $theline = "";
do {
$theline = <>;
} until ($theline !~ /^$/);
die if ($theline =~ /\-\-END Device Dump\-\-/);
print $indent.$theline;
dict($indent."\t", $1) if ($theline =~ /^.*[\t ]DICT[\t ]+(\d+)\n$/);
}
}
while (<>) { if ( /\-\-BEGIN Device Dump\-\-/ ) { print "----------------\n"; eval { dict("", -1); } } }
' < "$thefilename"
}
updateoverride () {
local thefilename="$1"
plutil -replace IODisplayEDID -data "$(echo -n $theedid | xxd -p -r | base64)" "${thefilename}"
plutil -replace DisplayProductID -integer $((0x${theedid:22:2}${theedid:20:2})) "${thefilename}"
plutil -replace DisplayVendorID -integer $((0x${theedid:16:4})) "${thefilename}"
}
makeoverride () {
local noedid=0
if [[ $1 == '-noedid' ]]; then
noedid=1
shift
fi
local thefilename="${theproductfile}"
if [[ $1 == '-m' ]]; then
thefilename="$themanufacturefile"
shift
fi
if [[ -z $1 ]]; then
[[ -d "${thevendordir}" ]] || mkdir "${thevendordir}"
else
thefilename="$1"
fi
[[ -f "${thefilename}" ]] && rm "${thefilename}"
/usr/libexec/PlistBuddy \
-c "Add :DisplayProductID integer ${theproductid}" \
-c "Add :DisplayVendorID integer ${thevendorid}" \
-c 'Add :IODisplayEDID data ""' \
-c 'Add :DisplayPixelDimensions data ""' \
"${thefilename}" > /dev/null
plutil -replace 'IODisplayEDID' -data "$(echo -n $theedid | xxd -p -r | base64)" "${thefilename}"
# the hex is big endian
plutil -replace 'DisplayPixelDimensions' -data "$(printf "%08x%08x" $maxresH $maxresV | xxd -p -r | base64)" "${thefilename}"
if (( noedid )); then
plutil -remove 'IODisplayEDID' "${thefilename}"
fi
# // IOGraphicsLibInternal.h flags for IOGFlags in override file
# enum {
# // disable any use of scaled modes,
# kOvrFlagDisableScaling = 0x00000001,
# // remove driver modes,
# kOvrFlagDisableNonScaled = 0x00000002,
# // disable scaled modes made up by the system (just use the override list)
# kOvrFlagDisableGenerated = 0x00000004
# };
}
installoverride () {
local thedir="/System"
local dstfile="$theproductfile"
local thefilename=""
while (( $# )); do
if [[ $1 == '-l' ]]; then
thedir=""
elif [[ $1 == '-m' ]]; then
dstfile="$themanufacturefile"
else
thefilename="$1"
fi
shift
done
[[ -n thefilename ]] && thefilename="${dstfile}"
mount | grep ' on / ' | grep -q 'read-only' && sudo mount -uw /
local fulldir="$thedir/Library/Displays/Contents/Resources/Overrides"
[[ -d "${fulldir}/${thevendordir}" ]] || sudo mkdir -p "${fulldir}/${thevendordir}"
sudo cp "${thefilename}" "${fulldir}/${dstfile}"
echo "# Installed file: ${fulldir}/${dstfile}"
if [[ "$(basename ${dstfile})" =~ "DisplayProductID-.*" ]]; then
[[ -f ${fulldir}/$themanufacturefile ]] && echo "# Warning: alternative override exists at ${fulldir}/$themanufacturefile"
else
[[ -f ${fulldir}/$theproductfile ]] && echo "# Warning: alternative override exists at ${fulldir}/$theproductfile"
fi
}
makemtdd () {
local donew=0
local thefilename=""
while (( $# )); do
case "$1" in
"-n") donew=1 ;;
*) thefilename="$1" ;;
esac
shift
done
if [[ -z ${thefilename} ]]; then
[[ -d "${thevendordir}" ]] || mkdir "${thevendordir}"
thefilename="${theproductfile}.mtdd"
fi
[[ -f "${thefilename}" ]] && rm "${thefilename}"
/usr/libexec/PlistBuddy \
-c 'Add display dict' \
-c 'Add :display:linkmode string multi-cable' \
-c 'Add :display:overlay data ""' \
-c "Add :display:streamcount integer $((tileCountH * tileCountV))" \
-c "Add :display:tileinfo string ($tileCountH,$tileCountV)" \
-c "Add :productid string $(printf "0x%04x" $theproductid)" \
-c "Add :serial integer 1" \
-c "Add :vendorid string $(printf "0x%04x" $thevendorid)" \
-c "Add :version string 1.3" \
"${thefilename}" > /dev/null
if (( donew )); then
plutil -insert 'display.backendtiming' -xml "<array>$(echo "$tileTimings" | perl -pe's/^[0-9]+ ([0-9]+)x([0-9]+)@([0-9.]+)Hz [0-9.]+kHz ([0-9]+)\.([0-9]+)MHz h\(([0-9]+) ([0-9]+) ([0-9]+) ([-+])\) v\(([0-9]+) ([0-9]+) ([0-9]+) ([-+])\).*/"<string>$1,$6,$7,".($1+$6+$7+$8)."x$2,$10,$11,".($2+$10+$11+$12)."\@$4${5}0000,$9$13<\/string>"/e')<string>*</string></array>" "${thefilename}"
plutil -insert 'display.frontendtiming' -xml "<array>$(echo "$tileTimings" | perl -pe's/^[0-9]+ ([0-9]+)x([0-9]+)@([0-9.]+)Hz [0-9.]+kHz ([0-9]+)\.([0-9]+)MHz h\(([0-9]+) ([0-9]+) ([0-9]+) ([-+])\) v\(([0-9]+) ([0-9]+) ([0-9]+) ([-+])\).*/"<string>".($1*2).",".($6*2).",".($7*2).",".($1+$6+$7+$8)."x$2,$10,$11,".($2+$10+$11+$12)."\@".("$4${5}0000" * 2).",$9$13<\/string>"/e')</array>" "${thefilename}"
else
plutil -insert 'display.backendtiming' -string "${tileSizeH}x${tileSizeV}@${tileRefresh}" "${thefilename}"
plutil -insert 'display.frontendtiming' -string "$((tileSizeH * 2))x${tileSizeV}@${tileRefresh}" "${thefilename}"
fi
plutil -replace 'display.overlay' -data "$(echo -n $theedid | xxd -p -r | base64)" "${thefilename}"
if (( HasAudio )); then
plutil -insert 'display.audio' -string '(1,1)' "${thefilename}"
fi
echo "# Created mtdd file: ${thefilename}"
}
makepatch () {
local thefilename="$1"
if [[ -z ${thefilename} ]]; then
[[ -d "${thevendordir}" ]] || mkdir "${thevendordir}"
thefilename="${theproductfile}"
fi
[[ -f "${thefilename}" ]] && rm "${thefilename}"
/usr/libexec/PlistBuddy \
-c "Add :DisplayProductID integer ${theproductid}" \
-c "Add :DisplayVendorID integer ${thevendorid}" \
-c 'Add edid-patches array' \
"${thefilename}" > /dev/null
local i=""
for ((i = 0 ; i < ${#patches[@]} ; i++)); do
local thepatch="${patches[i+arrstart]}"
local offset=$((${thepatch%:*}))
local data=${thepatch#*:}
plutil -insert "edid-patches.$i" -xml "<dict>
<key>offset</key>
<integer>$offset</integer>
<key>data</key>
<data>$(echo -n $data | xxd -p -r | base64)</data>
</dict>" "${thefilename}"
done
echo "# Created patch file: ${thefilename}"
}
makemtddall () {
local saveedid="$theedid"
local thefolderpath="$1"
local i=""
for ((i = 1 ; i <= ${#edids[@]} ; i++)); do
useedidnum $i
[[ -d "${thefolderpath:=.}/${thevendordir}" ]] || mkdir "${thefolderpath}/${thevendordir}"
makemtdd "${thefolderpath}/$theproductfile".mtdd
done
useedidstring "$saveedid"
}
translateoui () {
local oui=$1
if [[ $oui =~ "([0-9]+)-([0-9]+)-([0-9]+)" ]]; then
oui=$(eval $(echo -n "$oui" | sed -E '/([0-9]+)-([0-9]+)-([0-9]+)/s//printf "%02X%02X%02X" \1 \2 \3/'))
else
oui=${oui//-/}
oui=${oui//0x/}
oui=$(printf "%06X" $((0x$oui)))
fi
for thetype in oui cid; do
[[ -f ~/${thetype}.txt ]] || curl -s "http://standards-oui.ieee.org/${thetype}/${thetype}.txt" > ~/${thetype}.txt
local thetranslate="$(sed -nE '/^'$oui'[ ]+\(base 16\)*(.*)/s//\1/p' ~/${thetype}.txt | tr -d '\t\r')"
[[ -n $thetranslate ]] && echo "$thetype: 0x$oui = $thetranslate"
done
}
translatevendor () {
local thevendorid=$(($1))
local vendorascii=$(printf "%06x" $((((thevendorid&0x7c00)<<6)+((thevendorid&0x3e0)<<3)+(thevendorid&0x1f)+0x404040)) | xxd -p -r)
if [[ ! -f ~/pnp_id_list.txt ]]; then
curl -s "https://uefi.org/pnp_id_list" | \
sed -E '/(<\/?)(header|footer)/s//\1span/' | \
tidy -wrap 0 -raw -utf8 -q | \
sed -nE '
/<tbody/,/<\/tbody/ {
/<tr/,/<\/tr/ {
/<tr.*/ { s/// ; h ; }
/<td.*title.*>(.*)<\/td>/ { s//3:\1/ ; H ; }
/<td.*pnp-id.*>(.*)<\/td>/ { s//2:\1/ ; H ; }
/<td.*pnp-approved-on-date.*>(.*)<\/span><\/td>/ { s//1:\1/ ; H ; }
/<\/tr/ {
g
# make field 1 first
s/(.*)(\n1:.*)/\2\1/
# swap field 2 and 3 if they are reversed
s/(\n3:.*)(\n2:.*)/\2\1/
# remove field numbers, replace linefeed with space
s/\n[1-3]:/ /g
# remove leading space
s/^ //
s/&amp;/\&/g
s/&nbsp;/ /g
s/ *$//
s/ / /g
p
}
}
}
' | \
sort -f -k 3 -k 2 -k 1 > ~/pnp_id_list.txt
fi
printf "0x%04x = %s = %s\n" $thevendorid "$vendorascii" "$(sed -nE '/[0-9/]+ '"$vendorascii"' (.*)/s//\1/p' ~/pnp_id_list.txt)"
# $(sed -nE '/^'$oui'[ ]+\(base 16\)*(.*)/s//\1/p' ~/oui.txt | tr -d '\t\r')
}
#=========================================================================================
edidhelp () {
echo '
Commands:
Get EDID
loadagdc [dstfilepath]
loadioreg
loadstring hexstring [sourcename]
loadbinfile filepath...
loadhexfile filepath... # reverses xxd
loadagdcfile filepath...
loadioregfile filepath...
loadoverridefile filepath...
loadmtddfile filepath...
loadswitchresxfile filepath...
loadmamfile filepath... # Monitor Asset Manager.txt
loadmamhexfile filepath... # Monitor Asset Manager.hex
loadmaminffile filepath... # Monitor Asset Manager.inf
loadmamdatfile filepath... # Monitor Asset Manager.dat
listedids
clearedids
Use EDID
useedidnum numberfromlist
useedidstring lowercasehexstring
Modify EDID
repairchecksums
replacebytes bytepos lowercasehexstring [numbytestoreplace]
deleteextensionblock blocknumber
deleteextensionblocktype blocktype(decimal or 0xhex)
adddisplayidextensionblock version producttype
addctaextensionblock version
removechromasubsampling
addchromasubsampling
cleardate
clearserialnumber
setpreferredisnative preferredvalue(0/1)
clearchromaticity
clearestablishedtimings
clearmanufacturerstimings
clearstandardtimings
deletectablocktype ctablocktype(decimal; +700 for extended)
createdetailedtiming MHz hactive hfront hpulse hback vactive[i=interlaced] vfront vpulse vback hsync(+/-) vsync(+/-) [hmm] [vmm] [hborder] [vborder]
createdummydescriptor
createemptydescriptor
replacedescriptor offset newdescriptor
createtype1timingdescriptor MHz hactive hfront hpulse hback vactive[i=interlaced] vfront vpulse vback hsync(+/-) vsync(+/-) [preferred(1/0)]
createtype1timingblock descriptors...
dumptype1timingdescriptor 20_byte_descriptor
deletedisplayidblock offset
deletedisplayidblocktype displayidblocktype(decimal or 0xhex)
adddisplayidblock offset newdisplayidblock... # -1 = insert before first DisplayID block; 0 = insert after last DisplayID block
applypatches filepath [sourcename]
Files from EDID
makemtdd [-n] [filepath]
The edid needs to be manually edited.
clearserialnumber
cleardate
#clearchromaticity
#clearestablishedtimings
#clearstandardtimings
#clearmanufacturerstimings
#removechromasubsampling
deletedisplayidblocktype 3 # remove type 1 DisplayID timings
deletedisplayidblocktype 0x12 # remove tile topology
# Add the deleted type 1 DisplayID timings, except replace the tile timing (backend timing) with a full size timing (frontend timing).
# Frontend timing in this example doubles the MHz and horizontal numbers of the backend timing.
adddisplayidblock 0 $(createtype1timingblock 9aa00104ff0ea0002f8021006f083e0003000500 $(createtype1timingdescriptor 1264.02 3840 96 64 20 2160 2 4 20 '+' '-' 1 ))
makepatch [filepath]
Creates an override file with edid-patches array from shell array variabled called patches. e.g. patches=("94:0818900a" "304:7f00080000000000000000")
makemtddall [folderpath]
makeoverride [-noedid] [-m | dstfilepath]
updateoverride filepath
installoverride [-l] [-m] [srcfilepath]
decode
decodeall [folderpath]
edidbin
edidbinall [folderpath]
dumpedid
dumpedidall [folderpath]
agdcdevicedump filepath
Miscellaneous functions
translateoui oui # AGDCDiagnose DisplayPort registers and EDIDs may have an oui.
Variables
dodump # Set to 1 to dump the EDID while changes are made to the EDID.
debug # Set to 1 to output debugging info (uses stderr)
DisplayIDblocks # An array of DisplayID blocks that are used in the EDID. Save these using something like saveDisplayIDblocks=($DisplayIDblocks) before modifying the EDID.
thetimings # a list of detailed timings (printf "%s\n" "${thetimings[@]}")
theproductfile # the name of an override file in the form of DisplayVendorID-%x/DisplayProductID-%x
themanufacturefile # the name of an override file in the form of DisplayVendorID-%x/DisplayYearManufacture-%d-DisplayWeekManufacture-%d
...
Help
edidhelp
'
}
#edidhelp
#=========================================================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment