blob: b945ee71a6a744ffe21d496a0d433dd033ecd9a0 [file] [log] [blame]
Dennis Kempin1a8a5be2013-06-18 11:00:02 -07001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
Dennis Kempin037675e2013-06-14 14:12:39 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4#
Dennis Kempin17766a62013-06-17 14:09:33 -07005""" This module manages the platform properties in mttools/platforms """
Dennis Kempin1a8a5be2013-06-18 11:00:02 -07006import json
Dennis Kempin037675e2013-06-14 14:12:39 -07007import os
8import re
Dennis Kempin1a8a5be2013-06-18 11:00:02 -07009import sys
Dennis Kempin037675e2013-06-14 14:12:39 -070010from xorg_conf import XorgInputClassParser
11
12# path to current script directory
13script_dir = os.path.dirname(os.path.realpath(__file__))
Dennis Kempin17766a62013-06-17 14:09:33 -070014platforms_dir = os.path.realpath(os.path.join(script_dir, '..', 'platforms'))
15xorg_conf_path = os.path.realpath(os.path.join(script_dir, '..',
16 '..', 'xorg-conf'))
Dennis Kempin037675e2013-06-14 14:12:39 -070017
18class PlatformProperties(object):
Dennis Kempin17766a62013-06-17 14:09:33 -070019 """ A class containing hardware and xorg properties for a platform.
20
21 The class can be created from an activity log or by providing
22 the name of the platform. Information will then be read from the
23 'platforms_dir' directory.
Dennis Kempin037675e2013-06-14 14:12:39 -070024 """
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070025 def __init__(self, platform=None, log=None):
26 self.required_axis = []
27 self.has_axis = []
28 self.device_class = "touchpad"
Dennis Kempin037675e2013-06-14 14:12:39 -070029 if platform:
30 basename = os.path.join(platforms_dir, platform)
31 self.name = platform
Dennis Kempin17766a62013-06-17 14:09:33 -070032 self.hwprops_file = basename + '.hwprops'
33 self.props_file = basename + '.props'
Dennis Kempin037675e2013-06-14 14:12:39 -070034 self.xorg_parser = XorgInputClassParser()
35 self._ParseHWProperties(open(self.hwprops_file).read())
36 self._ParseProperties(open(self.props_file).read())
37 self._UpdateDimensions()
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070038 elif log:
Dennis Kempin17766a62013-06-17 14:09:33 -070039 self.name = ''
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070040 self._ParseActivityLog(log.activity)
41 if log.evdev:
42 self._ParseEvdevLog(log.evdev)
Dennis Kempin037675e2013-06-14 14:12:39 -070043
44 def _ParseActivityLog(self, activity_data):
Dennis Kempin17766a62013-06-17 14:09:33 -070045 """ Parse property information from an activity log """
Dennis Kempin037675e2013-06-14 14:12:39 -070046 activity = json.loads(activity_data)
Dennis Kempin17766a62013-06-17 14:09:33 -070047 self.properties = activity['properties']
Dennis Kempin037675e2013-06-14 14:12:39 -070048
Dennis Kempin17766a62013-06-17 14:09:33 -070049 hwprops = activity['hardwareProperties']
50 self.x_min = int(hwprops['left'])
51 self.x_max = int(hwprops['right'])
52 self.x_res = int(hwprops['xResolution'])
53 self.y_min = int(hwprops['top'])
54 self.y_max = int(hwprops['bottom'])
55 self.y_res = int(hwprops['yResolution'])
Dennis Kempin037675e2013-06-14 14:12:39 -070056
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070057 def _ParseEvdevLog(self, evdev_data):
Dennis Kempin143ef162014-04-09 13:46:50 -070058 # Look for embedded hwproperties in header. Format:
59 # absinfo: axis min max 0 0 res
60 abs_regex = 5 * ' ([0-9]+)'
61 xregex = re.compile('# absinfo: 53' + abs_regex)
62 xmatch = xregex.search(evdev_data);
63 self.x_min = int(xmatch.group(1))
64 self.x_max = int(xmatch.group(2))
65 self.x_res = int(xmatch.group(5))
66
67 yregex = re.compile('# absinfo: 54' + abs_regex)
68 ymatch = yregex.search(evdev_data);
69 self.y_min = int(ymatch.group(1))
70 self.y_max = int(ymatch.group(2))
71 self.y_res = int(ymatch.group(5))
72
73 axis_regex = re.compile('# absinfo: ([0-9]+)')
74 for match in axis_regex.finditer(evdev_data):
75 self.has_axis.append(int(match.group(1)))
76
77 # look for axes used in the log itself.
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070078 # The format of ABS (0003) reports is:
79 # timestamp 0003 axis value
80 report_regex = re.compile(' 0003 ([0-9a-f]{4}) ([0-9a-f]+)')
81 for match in report_regex.finditer(evdev_data):
82 axis = int(match.group(1), 16)
83 if axis not in self.required_axis:
84 self.required_axis.append(axis)
85
Dennis Kempin143ef162014-04-09 13:46:50 -070086
Dennis Kempin037675e2013-06-14 14:12:39 -070087 def _ParseHWProperties(self, data):
Dennis Kempin17766a62013-06-17 14:09:33 -070088 """ Parse x and y dimensions and resolution from hwprops file """
Dennis Kempin143ef162014-04-09 13:46:50 -070089 abs_regex = 5 * ' ([0-9\-]+)'
90 xregex = re.compile('A: 35' + abs_regex)
Dennis Kempin037675e2013-06-14 14:12:39 -070091 xmatch = xregex.search(data);
92 self.x_min = int(xmatch.group(1))
93 self.x_max = int(xmatch.group(2))
94 self.x_res = int(xmatch.group(5))
95
Dennis Kempin143ef162014-04-09 13:46:50 -070096 yregex = re.compile('A: 36' + abs_regex)
Dennis Kempin037675e2013-06-14 14:12:39 -070097 ymatch = yregex.search(data);
98 self.y_min = int(ymatch.group(1))
99 self.y_max = int(ymatch.group(2))
100 self.y_res = int(ymatch.group(5))
101
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700102 axis_regex = re.compile('A: ([0-9a-f]+)')
103 for match in axis_regex.finditer(data):
104 self.has_axis.append(int(match.group(1), 16))
105
Dennis Kempin037675e2013-06-14 14:12:39 -0700106 def _ParseProperties(self, data):
Dennis Kempin17766a62013-06-17 14:09:33 -0700107 """ Parse properties from file and inject xorg properties. """
Dennis Kempin037675e2013-06-14 14:12:39 -0700108 self.properties = {}
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700109 self.ignore_properties = []
Dennis Kempin037675e2013-06-14 14:12:39 -0700110 data = json.loads(data)
111
Dennis Kempin17766a62013-06-17 14:09:33 -0700112 if 'gestures' in data:
113 self.properties.update(data['gestures'])
Dennis Kempin037675e2013-06-14 14:12:39 -0700114
Dennis Kempin17766a62013-06-17 14:09:33 -0700115 if 'device_class' in data:
116 self.device_class = data['device_class']
Dennis Kempin037675e2013-06-14 14:12:39 -0700117
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700118 if 'ignore' in data:
119 self.ignore_properties.extend(data['ignore'])
120
Dennis Kempin17766a62013-06-17 14:09:33 -0700121 if xorg_conf_path and 'xorg' in data and 'file' in data['xorg']:
122 filename = os.path.join(xorg_conf_path, data['xorg']['file'])
Dennis Kempin037675e2013-06-14 14:12:39 -0700123 input_classes = self.xorg_parser.Parse(file=filename)
Dennis Kempin17766a62013-06-17 14:09:33 -0700124 if 'identifier' in data['xorg']:
125 properties = input_classes[data['xorg']['identifier']]
Dennis Kempin037675e2013-06-14 14:12:39 -0700126 self.properties.update(properties)
Dennis Kempin17766a62013-06-17 14:09:33 -0700127 if 'identifiers' in data['xorg']:
128 for identifier in data['xorg']['identifiers']:
Dennis Kempin037675e2013-06-14 14:12:39 -0700129 properties = input_classes[identifier]
130 self.properties.update(properties)
131
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700132 for prop in self.ignore_properties:
133 if prop in self.properties:
134 del self.properties[prop]
135
Dennis Kempin037675e2013-06-14 14:12:39 -0700136 def _UpdateDimensions(self):
Dennis Kempin17766a62013-06-17 14:09:33 -0700137 """ Update x/y min/max with xorg properties.
138
139 CMT allows hardware properties to be overwritten by xorg properties.
140 Do the same in this class.
Dennis Kempin037675e2013-06-14 14:12:39 -0700141 """
Dennis Kempin17766a62013-06-17 14:09:33 -0700142 if 'Active Area Left' in self.properties:
143 self.x_min = int(self.properties['Active Area Left'])
144 if 'Active Area Right' in self.properties:
145 self.x_max = int(self.properties['Active Area Right'])
Dennis Kempin17766a62013-06-17 14:09:33 -0700146 if 'Active Area Top' in self.properties:
147 self.y_min = int(self.properties['Active Area Top'])
148 if 'Active Area Bottom' in self.properties:
149 self.y_max = int(self.properties['Active Area Bottom'])
Dennis Kempin55af9cc2013-06-20 15:07:21 -0700150
151 if 'Horizontal Resolution' in self.properties:
152 self.x_res = int(self.properties['Horizontal Resolution'])
Dennis Kempin17766a62013-06-17 14:09:33 -0700153 if 'Vertical Resolution' in self.properties:
154 self.y_res = int(self.properties['Vertical Resolution'])
Dennis Kempin037675e2013-06-14 14:12:39 -0700155
Dennis Kempin55af9cc2013-06-20 15:07:21 -0700156 if 'SemiMT Non Linear Area Left' in self.properties:
157 self.x_min = int(self.properties['SemiMT Non Linear Area Left'])
158 if 'SemiMT Non Linear Area Right' in self.properties:
159 self.x_max = int(self.properties['SemiMT Non Linear Area Right'])
160 if 'SemiMT Non Linear Area Top' in self.properties:
161 self.y_min = int(self.properties['SemiMT Non Linear Area Top'])
162 if 'SemiMT Non Linear Area Bottom' in self.properties:
163 self.y_max = int(self.properties['SemiMT Non Linear Area Bottom'])
164
165
166 def Match(self, other, loose, debug=False):
Dennis Kempin17766a62013-06-17 14:09:33 -0700167 """ Compare properties and return similarity.
168
169 Compare these properties to another PlatformProperties instance.
170 The return value is a score between 1. 0 meaning there is a big mismatch
171 and 1 meaning the properties match completely.
172 Only a selected range of properties are compared in order to
173 prevent property adjustments to cause platforms to be mismatched.
Dennis Kempin037675e2013-06-14 14:12:39 -0700174 """
175 scores = []
176 def compare(a, b, what):
177 value = abs(float(a) - float(b))
178 if value > 0:
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700179 value = min(1, value / max(abs(float(a)), abs(float(b))))
Dennis Kempin037675e2013-06-14 14:12:39 -0700180 scores.append(1-value)
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700181 if debug:
182 print "%s: %s == %s" % (what, str(a), str(b))
Dennis Kempin037675e2013-06-14 14:12:39 -0700183 def compare_attr(what):
184 compare(getattr(self, what), getattr(other, what), what)
185 def compare_prop(what):
186 if what not in self.properties or what not in other.properties:
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700187 scores.append(0)
188 else:
189 compare(self.properties[what], other.properties[what], what)
190 def check_axis(required, available):
191 for axis in required:
192 if axis not in available:
193 scores.append(0)
194 return
195
Dennis Kempin17766a62013-06-17 14:09:33 -0700196 compare_attr('x_min')
197 compare_attr('x_max')
198 compare_attr('x_res')
Dennis Kempin55af9cc2013-06-20 15:07:21 -0700199 compare_attr('y_min')
200 compare_attr('y_max')
201 compare_attr('y_res')
202 if not loose:
203 compare_prop('Pressure Calibration Offset')
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700204
205 if self.required_axis:
206 if debug:
207 print "axis:", self.required_axis, "in", other.has_axis
208 check_axis(self.required_axis, other.has_axis)
209
210 if other.required_axis:
211 if debug:
212 print "axis:", other.required_axis, "in", self.has_axis
213 check_axis(other.required_axis, self.has_axis)
214
Dennis Kempin037675e2013-06-14 14:12:39 -0700215 return reduce(lambda x,y: (x * y), scores)
216
217class PlatformDatabase(object):
Dennis Kempin17766a62013-06-17 14:09:33 -0700218 """ Class for managing platforms.
219
220 This class reads all available platforms from the platforms_dir and allows
221 to search for matching platforms to an activity log file.
Dennis Kempin037675e2013-06-14 14:12:39 -0700222 """
223 def __init__(self):
224 platform_files = [ f for f in os.listdir(platforms_dir)
Dennis Kempin17766a62013-06-17 14:09:33 -0700225 if f.endswith('.hwprops') ]
Dennis Kempin037675e2013-06-14 14:12:39 -0700226 self.platforms = {}
227 for file in platform_files:
Dennis Kempin17766a62013-06-17 14:09:33 -0700228 name = file.replace('.hwprops', '')
Dennis Kempin037675e2013-06-14 14:12:39 -0700229 self.platforms[name] = PlatformProperties(platform=name)
230
Dennis Kempin55af9cc2013-06-20 15:07:21 -0700231 def FindMatching(self, log, loose=True, debug=False):
Dennis Kempin17766a62013-06-17 14:09:33 -0700232 """ Find platform matching activity_data.
233
234 Returns the PlatformProperties instance of the platform matching
235 the activity log data. This method might terminate the program in
236 case no match can be made, or the match is ambiguous.
Dennis Kempin037675e2013-06-14 14:12:39 -0700237 """
238 result = None
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700239 properties = PlatformProperties(log=log)
Dennis Kempin037675e2013-06-14 14:12:39 -0700240 for name, platform in self.platforms.items():
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700241 if debug:
242 print "#" * 10, name
Dennis Kempin55af9cc2013-06-20 15:07:21 -0700243 score = platform.Match(properties, loose, debug)
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700244 if debug:
245 print name, "score =", score
Dennis Kempin037675e2013-06-14 14:12:39 -0700246 if score > 0.9:
247 if result:
Dennis Kempin55af9cc2013-06-20 15:07:21 -0700248 if loose:
249 if debug:
250 print "-" * 10, "Multiple matches. Try strict"
251 return self.FindMatching(log, False, debug)
Dennis Kempin17766a62013-06-17 14:09:33 -0700252 print ('multiple matching platforms:', result.name,
253 'and', platform.name)
Dennis Kempin19e972b2013-06-20 13:21:38 -0700254 return None
Dennis Kempin037675e2013-06-14 14:12:39 -0700255 result = platform
256 if not result:
Dennis Kempin17766a62013-06-17 14:09:33 -0700257 print 'cannot find matching platform'
Dennis Kempin19e972b2013-06-20 13:21:38 -0700258 return None
Dennis Kempin037675e2013-06-14 14:12:39 -0700259 return result