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