• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python2
2# dtd_parser.py - DTD structure parser
3#
4# SPDX-License-Identifier: GPL-2.0-only
5
6'''
7DTD string parser/generator.
8
9Detailed timing descriptor (DTD) is an 18 byte array describing video mode
10(screen resolution, display properties, etc.) in EDID and used by Intel Option
11ROM. Option ROM can support multiple video modes, specific mode is picked by
12the BIOS through the appropriate Option ROM callback function.
13
14This program allows to interpret the 18 byte hex DTD dump, and/or modify
15certain values and generate a new DTD.
16'''
17
18import 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
38dtd_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
59PREFIX = 'attr_'
60
61class 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
172def 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
191if __name__ == '__main__':
192    try:
193        main(sys.argv[1:])
194    except (ValueError, IndexError):
195        print """
196A 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)
200