blob: 2ebabad56616fe6285844d9520aa7ff61a9c3cfe [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):
58 # The format of ABS (0003) reports is:
59 # timestamp 0003 axis value
60 report_regex = re.compile(' 0003 ([0-9a-f]{4}) ([0-9a-f]+)')
61 for match in report_regex.finditer(evdev_data):
62 axis = int(match.group(1), 16)
63 if axis not in self.required_axis:
64 self.required_axis.append(axis)
65
Dennis Kempin037675e2013-06-14 14:12:39 -070066 def _ParseHWProperties(self, data):
Dennis Kempin17766a62013-06-17 14:09:33 -070067 """ Parse x and y dimensions and resolution from hwprops file """
68 xregex = re.compile('A: 35 ([0-9\-]+) ([0-9\-]+) ([0-9\-]+) ' +
69 '([0-9\-]+) ([0-9\-]+)')
Dennis Kempin037675e2013-06-14 14:12:39 -070070 xmatch = xregex.search(data);
71 self.x_min = int(xmatch.group(1))
72 self.x_max = int(xmatch.group(2))
73 self.x_res = int(xmatch.group(5))
74
Dennis Kempin17766a62013-06-17 14:09:33 -070075 yregex = re.compile('A: 36 ([0-9\-]+) ([0-9\-]+) ([0-9\-]+) ' +
76 '([0-9\-]+) ([0-9\-]+)')
Dennis Kempin037675e2013-06-14 14:12:39 -070077 ymatch = yregex.search(data);
78 self.y_min = int(ymatch.group(1))
79 self.y_max = int(ymatch.group(2))
80 self.y_res = int(ymatch.group(5))
81
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070082 axis_regex = re.compile('A: ([0-9a-f]+)')
83 for match in axis_regex.finditer(data):
84 self.has_axis.append(int(match.group(1), 16))
85
Dennis Kempin037675e2013-06-14 14:12:39 -070086 def _ParseProperties(self, data):
Dennis Kempin17766a62013-06-17 14:09:33 -070087 """ Parse properties from file and inject xorg properties. """
Dennis Kempin037675e2013-06-14 14:12:39 -070088 self.properties = {}
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070089 self.ignore_properties = []
Dennis Kempin037675e2013-06-14 14:12:39 -070090 data = json.loads(data)
91
Dennis Kempin17766a62013-06-17 14:09:33 -070092 if 'gestures' in data:
93 self.properties.update(data['gestures'])
Dennis Kempin037675e2013-06-14 14:12:39 -070094
Dennis Kempin17766a62013-06-17 14:09:33 -070095 if 'device_class' in data:
96 self.device_class = data['device_class']
Dennis Kempin037675e2013-06-14 14:12:39 -070097
Dennis Kempin1a8a5be2013-06-18 11:00:02 -070098 if 'ignore' in data:
99 self.ignore_properties.extend(data['ignore'])
100
Dennis Kempin17766a62013-06-17 14:09:33 -0700101 if xorg_conf_path and 'xorg' in data and 'file' in data['xorg']:
102 filename = os.path.join(xorg_conf_path, data['xorg']['file'])
Dennis Kempin037675e2013-06-14 14:12:39 -0700103 input_classes = self.xorg_parser.Parse(file=filename)
Dennis Kempin17766a62013-06-17 14:09:33 -0700104 if 'identifier' in data['xorg']:
105 properties = input_classes[data['xorg']['identifier']]
Dennis Kempin037675e2013-06-14 14:12:39 -0700106 self.properties.update(properties)
Dennis Kempin17766a62013-06-17 14:09:33 -0700107 if 'identifiers' in data['xorg']:
108 for identifier in data['xorg']['identifiers']:
Dennis Kempin037675e2013-06-14 14:12:39 -0700109 properties = input_classes[identifier]
110 self.properties.update(properties)
111
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700112 for prop in self.ignore_properties:
113 if prop in self.properties:
114 del self.properties[prop]
115
Dennis Kempin037675e2013-06-14 14:12:39 -0700116 def _UpdateDimensions(self):
Dennis Kempin17766a62013-06-17 14:09:33 -0700117 """ Update x/y min/max with xorg properties.
118
119 CMT allows hardware properties to be overwritten by xorg properties.
120 Do the same in this class.
Dennis Kempin037675e2013-06-14 14:12:39 -0700121 """
Dennis Kempin17766a62013-06-17 14:09:33 -0700122 if 'Active Area Left' in self.properties:
123 self.x_min = int(self.properties['Active Area Left'])
124 if 'Active Area Right' in self.properties:
125 self.x_max = int(self.properties['Active Area Right'])
126 if 'Horizontal Resolution' in self.properties:
127 self.x_res = int(self.properties['Horizontal Resolution'])
128 if 'Active Area Top' in self.properties:
129 self.y_min = int(self.properties['Active Area Top'])
130 if 'Active Area Bottom' in self.properties:
131 self.y_max = int(self.properties['Active Area Bottom'])
132 if 'Vertical Resolution' in self.properties:
133 self.y_res = int(self.properties['Vertical Resolution'])
Dennis Kempin037675e2013-06-14 14:12:39 -0700134
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700135 def Match(self, other, debug=False):
Dennis Kempin17766a62013-06-17 14:09:33 -0700136 """ Compare properties and return similarity.
137
138 Compare these properties to another PlatformProperties instance.
139 The return value is a score between 1. 0 meaning there is a big mismatch
140 and 1 meaning the properties match completely.
141 Only a selected range of properties are compared in order to
142 prevent property adjustments to cause platforms to be mismatched.
Dennis Kempin037675e2013-06-14 14:12:39 -0700143 """
144 scores = []
145 def compare(a, b, what):
146 value = abs(float(a) - float(b))
147 if value > 0:
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700148 value = min(1, value / max(abs(float(a)), abs(float(b))))
Dennis Kempin037675e2013-06-14 14:12:39 -0700149 scores.append(1-value)
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700150 if debug:
151 print "%s: %s == %s" % (what, str(a), str(b))
Dennis Kempin037675e2013-06-14 14:12:39 -0700152 def compare_attr(what):
153 compare(getattr(self, what), getattr(other, what), what)
154 def compare_prop(what):
155 if what not in self.properties or what not in other.properties:
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700156 scores.append(0)
157 else:
158 compare(self.properties[what], other.properties[what], what)
159 def check_axis(required, available):
160 for axis in required:
161 if axis not in available:
162 scores.append(0)
163 return
164
Dennis Kempin17766a62013-06-17 14:09:33 -0700165 compare_attr('x_min')
166 compare_attr('x_max')
167 compare_attr('x_res')
168 compare_prop('Pressure Calibration Offset')
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700169
170 if self.required_axis:
171 if debug:
172 print "axis:", self.required_axis, "in", other.has_axis
173 check_axis(self.required_axis, other.has_axis)
174
175 if other.required_axis:
176 if debug:
177 print "axis:", other.required_axis, "in", self.has_axis
178 check_axis(other.required_axis, self.has_axis)
179
Dennis Kempin037675e2013-06-14 14:12:39 -0700180 return reduce(lambda x,y: (x * y), scores)
181
182class PlatformDatabase(object):
Dennis Kempin17766a62013-06-17 14:09:33 -0700183 """ Class for managing platforms.
184
185 This class reads all available platforms from the platforms_dir and allows
186 to search for matching platforms to an activity log file.
Dennis Kempin037675e2013-06-14 14:12:39 -0700187 """
188 def __init__(self):
189 platform_files = [ f for f in os.listdir(platforms_dir)
Dennis Kempin17766a62013-06-17 14:09:33 -0700190 if f.endswith('.hwprops') ]
Dennis Kempin037675e2013-06-14 14:12:39 -0700191 self.platforms = {}
192 for file in platform_files:
Dennis Kempin17766a62013-06-17 14:09:33 -0700193 name = file.replace('.hwprops', '')
Dennis Kempin037675e2013-06-14 14:12:39 -0700194 self.platforms[name] = PlatformProperties(platform=name)
195
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700196 def FindMatching(self, log, debug=False):
Dennis Kempin17766a62013-06-17 14:09:33 -0700197 """ Find platform matching activity_data.
198
199 Returns the PlatformProperties instance of the platform matching
200 the activity log data. This method might terminate the program in
201 case no match can be made, or the match is ambiguous.
Dennis Kempin037675e2013-06-14 14:12:39 -0700202 """
203 result = None
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700204 properties = PlatformProperties(log=log)
Dennis Kempin037675e2013-06-14 14:12:39 -0700205 for name, platform in self.platforms.items():
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700206 if debug:
207 print "#" * 10, name
Dennis Kempin037675e2013-06-14 14:12:39 -0700208 score = platform.Match(properties)
Dennis Kempin1a8a5be2013-06-18 11:00:02 -0700209 if debug:
210 print name, "score =", score
Dennis Kempin037675e2013-06-14 14:12:39 -0700211 if score > 0.9:
212 if result:
Dennis Kempin17766a62013-06-17 14:09:33 -0700213 print ('multiple matching platforms:', result.name,
214 'and', platform.name)
Dennis Kempin19e972b2013-06-20 13:21:38 -0700215 return None
Dennis Kempin037675e2013-06-14 14:12:39 -0700216 result = platform
217 if not result:
Dennis Kempin17766a62013-06-17 14:09:33 -0700218 print 'cannot find matching platform'
Dennis Kempin19e972b2013-06-20 13:21:38 -0700219 return None
Dennis Kempin037675e2013-06-14 14:12:39 -0700220 return result