Nico Huber | ca41a6a | 2016-01-23 21:29:47 +0100 | [diff] [blame] | 1 | #!/usr/bin/env python2 |
Vadim Bendebury | d0b860a | 2012-08-27 11:19:55 -0700 | [diff] [blame] | 2 | # dtd_parser.py - DTD structure parser |
| 3 | # |
Patrick Georgi | 7333a11 | 2020-05-08 20:48:04 +0200 | [diff] [blame] | 4 | # SPDX-License-Identifier: GPL-2.0-only |
Vadim Bendebury | d0b860a | 2012-08-27 11:19:55 -0700 | [diff] [blame] | 5 | |
| 6 | ''' |
| 7 | DTD string parser/generator. |
| 8 | |
| 9 | Detailed timing descriptor (DTD) is an 18 byte array describing video mode |
| 10 | (screen resolution, display properties, etc.) in EDID and used by Intel Option |
| 11 | ROM. Option ROM can support multiple video modes, specific mode is picked by |
| 12 | the BIOS through the appropriate Option ROM callback function. |
| 13 | |
| 14 | This program allows to interpret the 18 byte hex DTD dump, and/or modify |
| 15 | certain values and generate a new DTD. |
| 16 | ''' |
| 17 | |
| 18 | import sys |
| 19 | |
| 20 | # |
| 21 | # The DTD array format description can be found in |
| 22 | # http://en.wikipedia.org/wiki/Extended_display_identification_data, (see the |
| 23 | # EDID Detailed Timing Descriptor section). |
| 24 | # |
| 25 | # The below dictionary describes how different DTD parameters are laid out in |
| 26 | # the array. Note that many parameters span multiple bit fields in the DTD. |
| 27 | # |
| 28 | # The keys in the dictionary are stings (field names), the values are tuples |
| 29 | # of either numbers or tri-tuples. If the element of the tuple is a number, it |
| 30 | # is the offset in DTD, and the entire byte is used in this field. If the |
| 31 | # element is a tri-tuple, its components are (DTD offset, bit shift, field |
| 32 | # width). |
| 33 | # |
| 34 | # The partial values are extracted from the DTD fields and concatenated |
| 35 | # together to form actual parameter value. |
| 36 | # |
| 37 | |
| 38 | dtd_descriptor = { |
| 39 | 'dclck' : (1, 0), |
| 40 | 'hor_active' : ((4, 4, 4), 2), |
| 41 | 'hor_blank' : ((4, 0, 4), 3), |
| 42 | 'vert_act' : ((7, 4, 4), 5), |
| 43 | 'vert_blank' : ((7, 0, 4), 6), |
| 44 | 'hsync_offset' : ((11, 6, 2), 8), |
| 45 | 'hsync_pulse_width' : ((11, 4, 2), 9), |
| 46 | 'vsync_offset' : ((11, 2, 2), (10, 4, 4)), |
| 47 | 'vsync_pulse_width' : ((11, 0, 2), (10, 0, 4)), |
| 48 | 'hor_image_size' : ((14, 4, 4), 12), |
| 49 | 'vert_image_size' : ((14, 0, 4), 13), |
| 50 | 'hor_border' : (15,), |
| 51 | 'vert_border' : (16,), |
| 52 | 'interlaced' : ((17, 7, 1),), |
| 53 | 'reserved' : ((17, 5, 2), (17, 0, 1)), |
| 54 | 'digital_separate' : ((17, 3, 2),), |
| 55 | 'vert_polarity' : ((17, 2, 1),), |
| 56 | 'hor_polarity' : ((17, 1, 1),), |
| 57 | } |
| 58 | |
| 59 | PREFIX = 'attr_' |
| 60 | |
| 61 | class DTD(object): |
| 62 | '''An object containing all DTD information. |
| 63 | |
| 64 | The attributes are created dynamically when the input DTD string is |
| 65 | parsed. For each element of the above dictionary two attributes are added: |
| 66 | |
| 67 | 'attr_<param>' to hold the actual parameter value |
| 68 | 'max_attr_<param>' to hold the maximum allowed value for this parameter. |
| 69 | ''' |
| 70 | |
| 71 | def __init__(self): |
| 72 | for name in dtd_descriptor: |
| 73 | setattr(self, PREFIX + name, 0) |
| 74 | |
| 75 | def init(self, sarray): |
| 76 | '''Initialize the object with values from a DTD array. |
| 77 | |
| 78 | Inputs: |
| 79 | |
| 80 | sarray: a string, an array of ASCII hex representations of the 18 DTD |
| 81 | bytes. |
| 82 | |
| 83 | Raises: implicitly raises ValueError or IndexError exceptions in case |
| 84 | the input string has less than 18 elements, or some of the |
| 85 | elements can not be converted to integer. |
| 86 | ''' |
| 87 | |
| 88 | harray = [int(x, 16) for x in sarray] |
| 89 | for name, desc in dtd_descriptor.iteritems(): |
| 90 | attr_value = 0 |
| 91 | total_width = 0 |
| 92 | for tup in desc: |
| 93 | if isinstance(tup, tuple): |
| 94 | offset, shift, width = tup |
| 95 | else: |
| 96 | offset, shift, width = tup, 0, 8 |
| 97 | |
| 98 | mask = (1 << width) - 1 |
| 99 | attr_value = (attr_value << width) + ( |
| 100 | (harray[offset] >> shift) & mask) |
| 101 | total_width += width |
| 102 | setattr(self, PREFIX + name, attr_value) |
| 103 | setattr(self, 'max_' + PREFIX + name, (1 << total_width) - 1) |
| 104 | |
| 105 | def __str__(self): |
| 106 | text = [] |
| 107 | for name in sorted(dtd_descriptor.keys()): |
| 108 | text.append('%20s: %d' % (name, getattr(self, PREFIX + name))) |
| 109 | return '\n'.join(text) |
| 110 | |
| 111 | def inhex(self): |
| 112 | '''Generate contents of the DTD as a 18 byte ASCII hex array.''' |
| 113 | |
| 114 | result = [0] * 18 |
| 115 | for name, desc in dtd_descriptor.iteritems(): |
| 116 | attr_value = getattr(self, PREFIX + name) |
| 117 | rdesc = list(desc) |
| 118 | rdesc.reverse() |
| 119 | for tup in rdesc: |
| 120 | if isinstance(tup, tuple): |
| 121 | offset, shift, width = tup |
| 122 | else: |
| 123 | offset, shift, width = tup, 0, 8 |
| 124 | |
| 125 | mask = (1 << width) - 1 |
| 126 | value = attr_value & mask |
| 127 | attr_value = attr_value >> width |
| 128 | result[offset] = (result[offset] & ~( |
| 129 | mask << shift)) | (value << shift) |
| 130 | |
| 131 | return ' '.join('%2.2x' % x for x in result) |
| 132 | |
| 133 | def handle_input(self, name): |
| 134 | '''Get user input and set a new parameter value if required. |
| 135 | |
| 136 | Display the parameter name, its current value, and prompt user for a |
| 137 | new value. |
| 138 | |
| 139 | If the user enters a dot, stop processing (return True). |
| 140 | |
| 141 | Empty user input means that this parameter does not have to change, |
| 142 | but the next parameter should be prompted. |
| 143 | |
| 144 | If input is non-empty, it is interpreted as a hex number, checked if |
| 145 | it fits the parameter and the new parameter value is set if checks |
| 146 | pass. |
| 147 | |
| 148 | Inputs: |
| 149 | |
| 150 | name - a string, parameter name, a key in dtd_descriptor |
| 151 | |
| 152 | Returns: |
| 153 | |
| 154 | Boolean, True meaning no more field are required to be modified, False |
| 155 | meaning that more field mods need to be prompted.. |
| 156 | ''' |
| 157 | |
| 158 | param = PREFIX + name |
| 159 | vmax = getattr(self, 'max_' + param) |
| 160 | new_value = raw_input('%s : %d ' % (name, getattr(self, param))) |
| 161 | if new_value == '': |
| 162 | return False |
| 163 | if new_value == '.': |
| 164 | return True |
| 165 | new_int = int(new_value) |
| 166 | if new_int > vmax: |
| 167 | print '%s exceeds maximum for %s (%d)' % (new_value, name, vmax) |
| 168 | else: |
| 169 | setattr(self, param, new_int) |
| 170 | return False |
| 171 | |
| 172 | def main(args): |
| 173 | if args[0] == '-m': |
| 174 | modify = True |
| 175 | base = 1 |
| 176 | else: |
| 177 | modify = False |
| 178 | base = 0 |
| 179 | |
| 180 | d = DTD() |
| 181 | d.init(args[base:]) |
| 182 | if modify: |
| 183 | for line in str(d).splitlines(): |
| 184 | if d.handle_input(line.split(':')[0].strip()): |
| 185 | break |
| 186 | print d |
| 187 | if modify: |
| 188 | print d.inhex() |
| 189 | |
| 190 | |
| 191 | if __name__ == '__main__': |
| 192 | try: |
| 193 | main(sys.argv[1:]) |
| 194 | except (ValueError, IndexError): |
| 195 | print """ |
| 196 | A string of 18 byte values in hex is required. |
| 197 | '-m' preceding the string will allow setting new parameter values. |
| 198 | """ |
| 199 | sys.exit(1) |