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