Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 1 | # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | # |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 5 | """ This module manages the platform properties in mttools/platforms """ |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 6 | import json |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 7 | import os |
| 8 | import re |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 9 | import sys |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 10 | from xorg_conf import XorgInputClassParser |
| 11 | |
| 12 | # path to current script directory |
| 13 | script_dir = os.path.dirname(os.path.realpath(__file__)) |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 14 | platforms_dir = os.path.realpath(os.path.join(script_dir, '..', 'platforms')) |
| 15 | xorg_conf_path = os.path.realpath(os.path.join(script_dir, '..', |
| 16 | '..', 'xorg-conf')) |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 17 | |
| 18 | class PlatformProperties(object): |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 19 | """ 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 24 | """ |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 25 | def __init__(self, platform=None, log=None): |
| 26 | self.required_axis = [] |
| 27 | self.has_axis = [] |
| 28 | self.device_class = "touchpad" |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 29 | if platform: |
| 30 | basename = os.path.join(platforms_dir, platform) |
| 31 | self.name = platform |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 32 | self.hwprops_file = basename + '.hwprops' |
| 33 | self.props_file = basename + '.props' |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 34 | self.xorg_parser = XorgInputClassParser() |
| 35 | self._ParseHWProperties(open(self.hwprops_file).read()) |
| 36 | self._ParseProperties(open(self.props_file).read()) |
| 37 | self._UpdateDimensions() |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 38 | elif log: |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 39 | self.name = '' |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 40 | self._ParseActivityLog(log.activity) |
| 41 | if log.evdev: |
| 42 | self._ParseEvdevLog(log.evdev) |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 43 | |
| 44 | def _ParseActivityLog(self, activity_data): |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 45 | """ Parse property information from an activity log """ |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 46 | activity = json.loads(activity_data) |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 47 | self.properties = activity['properties'] |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 48 | |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 49 | 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 56 | |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 57 | 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 66 | def _ParseHWProperties(self, data): |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 67 | """ 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 70 | 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 Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 75 | yregex = re.compile('A: 36 ([0-9\-]+) ([0-9\-]+) ([0-9\-]+) ' + |
| 76 | '([0-9\-]+) ([0-9\-]+)') |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 77 | 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 Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 82 | 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 86 | def _ParseProperties(self, data): |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 87 | """ Parse properties from file and inject xorg properties. """ |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 88 | self.properties = {} |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 89 | self.ignore_properties = [] |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 90 | data = json.loads(data) |
| 91 | |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 92 | if 'gestures' in data: |
| 93 | self.properties.update(data['gestures']) |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 94 | |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 95 | if 'device_class' in data: |
| 96 | self.device_class = data['device_class'] |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 97 | |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 98 | if 'ignore' in data: |
| 99 | self.ignore_properties.extend(data['ignore']) |
| 100 | |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 101 | 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 103 | input_classes = self.xorg_parser.Parse(file=filename) |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 104 | if 'identifier' in data['xorg']: |
| 105 | properties = input_classes[data['xorg']['identifier']] |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 106 | self.properties.update(properties) |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 107 | if 'identifiers' in data['xorg']: |
| 108 | for identifier in data['xorg']['identifiers']: |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 109 | properties = input_classes[identifier] |
| 110 | self.properties.update(properties) |
| 111 | |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 112 | for prop in self.ignore_properties: |
| 113 | if prop in self.properties: |
| 114 | del self.properties[prop] |
| 115 | |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 116 | def _UpdateDimensions(self): |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 117 | """ 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 121 | """ |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 122 | 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 134 | |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 135 | def Match(self, other, debug=False): |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 136 | """ 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 143 | """ |
| 144 | scores = [] |
| 145 | def compare(a, b, what): |
| 146 | value = abs(float(a) - float(b)) |
| 147 | if value > 0: |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 148 | value = min(1, value / max(abs(float(a)), abs(float(b)))) |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 149 | scores.append(1-value) |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 150 | if debug: |
| 151 | print "%s: %s == %s" % (what, str(a), str(b)) |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 152 | 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 Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 156 | 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 Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 165 | compare_attr('x_min') |
| 166 | compare_attr('x_max') |
| 167 | compare_attr('x_res') |
| 168 | compare_prop('Pressure Calibration Offset') |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 169 | |
| 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 180 | return reduce(lambda x,y: (x * y), scores) |
| 181 | |
| 182 | class PlatformDatabase(object): |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 183 | """ 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 187 | """ |
| 188 | def __init__(self): |
| 189 | platform_files = [ f for f in os.listdir(platforms_dir) |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 190 | if f.endswith('.hwprops') ] |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 191 | self.platforms = {} |
| 192 | for file in platform_files: |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 193 | name = file.replace('.hwprops', '') |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 194 | self.platforms[name] = PlatformProperties(platform=name) |
| 195 | |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 196 | def FindMatching(self, log, debug=False): |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 197 | """ 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 Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 202 | """ |
| 203 | result = None |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 204 | properties = PlatformProperties(log=log) |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 205 | for name, platform in self.platforms.items(): |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 206 | if debug: |
| 207 | print "#" * 10, name |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 208 | score = platform.Match(properties) |
Dennis Kempin | 1a8a5be | 2013-06-18 11:00:02 -0700 | [diff] [blame^] | 209 | if debug: |
| 210 | print name, "score =", score |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 211 | if score > 0.9: |
| 212 | if result: |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 213 | print ('multiple matching platforms:', result.name, |
| 214 | 'and', platform.name) |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 215 | sys.exit(-1) |
| 216 | result = platform |
| 217 | if not result: |
Dennis Kempin | 17766a6 | 2013-06-17 14:09:33 -0700 | [diff] [blame] | 218 | print 'cannot find matching platform' |
| 219 | sys.exit(-1) |
Dennis Kempin | 037675e | 2013-06-14 14:12:39 -0700 | [diff] [blame] | 220 | return result |