Home | History | Annotate | Download | only in firmware_TouchMTB
      1 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Touch device module provides some touch device related attributes."""
      6 
      7 import collections
      8 import glob
      9 import os
     10 import re
     11 import tempfile
     12 
     13 import common_util
     14 
     15 from firmware_constants import AXIS
     16 
     17 
     18 # Define AbsAxis class with axis attributes: min, max, and resolution
     19 AbsAxis = collections.namedtuple('AbsAxis', ['min', 'max', 'resolution'])
     20 
     21 
     22 class TouchDevice:
     23     """A class about touch device properties."""
     24     def __init__(self, device_node=None, is_touchscreen=False,
     25                  device_description_file=None):
     26         """If the device_description_file is provided (i.e., not None), it is
     27         used to create a mocked device for the purpose of replaying or
     28         unit tests.
     29         """
     30         self.device_node = (device_node if device_node else
     31                             self.get_device_node(is_touchscreen=is_touchscreen))
     32         self.device_description = self._get_device_description(
     33                 device_description_file)
     34         self.axis_x, self.axis_y = self.parse_abs_axes()
     35         self.axes = {AXIS.X: self.axis_x, AXIS.Y: self.axis_y}
     36 
     37     @staticmethod
     38     def xinput_helper(cmd):
     39         """A helper of xinput.sh to execute a command.
     40 
     41         This is a method copied from factory/py/test/utils.py
     42         """
     43         dummy_script = '. /opt/google/input/xinput.sh\n%s'
     44         with tempfile.NamedTemporaryFile(prefix='cros_touch_xinput.') as f:
     45             f.write(dummy_script % cmd)
     46             f.flush()
     47             return common_util.simple_system_output('sh %s' % f.name)
     48 
     49     @staticmethod
     50     def get_device_node(is_touchscreen=False):
     51         """Get the touch device node through xinput.
     52 
     53            Touchscreens have a different device name, so this
     54            chooses between them.  Otherwise they are the same.
     55 
     56            A device id is a simple integer say 12 extracted from a string like
     57                Atmel maXTouch Touchscreen   id=12   [floating slave]
     58 
     59            A device node is extracted from "xinput list-props device_id" and
     60            looks like
     61                Device Node (250):      "/dev/input/event8"
     62            In this example, the device node is /dev/input/event8
     63         """
     64         device_id = TouchDevice.xinput_helper(
     65                 'list_touchscreens' if is_touchscreen else 'list_touchpads')
     66         if device_id:
     67             device_node = TouchDevice.xinput_helper(
     68                     'device_get_prop %s "Device Node"' % device_id).strip('"')
     69         else:
     70             device_node = None
     71         return device_node
     72 
     73     def exists(self):
     74         """Indicate whether this device exists or not.
     75 
     76         Note that the device description is derived either from the provided
     77         device description file or from the system device node.
     78         """
     79         return bool(self.device_description)
     80 
     81     def get_dimensions_in_mm(self):
     82         """Get the width and height in mm of the device."""
     83         (left, right, top, bottom,
     84                 resolution_x, resolution_y) = self.get_resolutions()
     85         width = float((right - left)) / resolution_x
     86         height = float((bottom - top)) / resolution_y
     87         return (width, height)
     88 
     89     def get_resolutions(self):
     90         """Get the resolutions in x and y axis of the device."""
     91         return (self.axis_x.resolution, self.axis_y.resolution)
     92 
     93     def get_edges(self):
     94         """Get the left, right, top, and bottom edges of the device."""
     95         return (self.axis_x.min, self.axis_x.max,
     96                 self.axis_y.min, self.axis_y.max)
     97 
     98     def save_device_description_file(self, filepath, board):
     99         """Save the device description file in the specified filepath."""
    100         if self.device_description:
    101             # Replace the device name with the board name to reduce the risk
    102             # of leaking the touch device name which may be confidential.
    103             # Take the touchpad on link as an example:
    104             #   N: Atmel-maXTouch-Touchpad  would be replaced with
    105             #   N: link-touch-device
    106             name = 'N: %s-touch-device\n' % board
    107             try:
    108                 with open(filepath, 'w') as fo:
    109                     for line in self.device_description.splitlines():
    110                         fo.write(name if line.startswith('N:') else line + '\n')
    111                 return True
    112             except Exception as e:
    113                 msg = 'Error: %s in getting device description from %s'
    114                 print msg % (e, self.device_node)
    115         return False
    116 
    117     def _get_device_description(self, device_description_file):
    118         """Get the device description either from the specified device
    119         description file or from the system device node.
    120         """
    121         if device_description_file:
    122             # Get the device description from the device description file.
    123             try:
    124                 with open(device_description_file) as dd:
    125                     return dd.read()
    126             except Exception as e:
    127                 msg = 'Error: %s in opening the device description file: %s'
    128                 print msg % (e, device_description_file)
    129         elif self.device_node:
    130             # Get the device description from the device node.
    131             cmd = 'evemu-describe %s' % self.device_node
    132             try:
    133                 return common_util.simple_system_output(cmd)
    134             except Exception as e:
    135                 msg = 'Error: %s in getting the device description from %s'
    136                 print msg % (e, self.device_node)
    137         return None
    138 
    139     def parse_abs_axes(self):
    140         """Prase to get information about min, max, and resolution of
    141            ABS_X and ABS_Y
    142 
    143         Example of ABS_X:
    144                 A: 00 0 1280 0 0 12
    145         Example of ABS_y:
    146                 A: 01 0 1280 0 0 12
    147         """
    148         pattern = 'A:\s*%s\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)'
    149         pattern_x = pattern % '00'
    150         pattern_y = pattern % '01'
    151         axis_x = axis_y = None
    152         if self.device_description:
    153             for line in self.device_description.splitlines():
    154                 if not axis_x:
    155                     result = re.search(pattern_x, line, re.I)
    156                     if result:
    157                         min_x = int(result.group(1))
    158                         max_x = int(result.group(2))
    159                         resolution_x = int(result.group(5))
    160                         axis_x = AbsAxis(min_x, max_x, resolution_x)
    161                 if not axis_y:
    162                     result = re.search(pattern_y, line, re.I)
    163                     if result:
    164                         min_y = int(result.group(1))
    165                         max_y = int(result.group(2))
    166                         resolution_y = int(result.group(5))
    167                         axis_y = AbsAxis(min_y, max_y, resolution_y)
    168         return (axis_x, axis_y)
    169 
    170     def pixel_to_mm(self, (pixel_x, pixel_y)):
    171         """Convert the point coordinate from pixel to mm."""
    172         mm_x = float(pixel_x - self.axis_x.min) / self.axis_x.resolution
    173         mm_y = float(pixel_y - self.axis_y.min) / self.axis_y.resolution
    174         return (mm_x, mm_y)
    175 
    176     def pixel_to_mm_single_axis(self, value_pixel, axis):
    177         """Convert the coordinate from pixel to mm."""
    178         value_mm = float(value_pixel - axis.min) / axis.resolution
    179         return value_mm
    180 
    181     def pixel_to_mm_single_axis_by_name(self, value_pixel, axis_name):
    182         """Convert the coordinate from pixel to mm."""
    183         return self.pixel_to_mm_single_axis(value_pixel, self.axes[axis_name])
    184 
    185     def get_dimensions(self):
    186         """Get the vendor-specified dimensions of the touch device."""
    187         return (self.axis_x.max - self.axis_x.min,
    188                 self.axis_y.max - self.axis_y.min)
    189 
    190     def get_display_geometry(self, screen_size, display_ratio):
    191         """Get a preferred display geometry when running the test."""
    192         display_ratio = 0.8
    193         dev_width, dev_height = self.get_dimensions()
    194         screen_width, screen_height = screen_size
    195 
    196         if 1.0 * screen_width / screen_height <= 1.0 * dev_width / dev_height:
    197             disp_width = int(screen_width * display_ratio)
    198             disp_height = int(disp_width * dev_height / dev_width)
    199             disp_offset_x = 0
    200             disp_offset_y = screen_height - disp_height
    201         else:
    202             disp_height = int(screen_height * display_ratio)
    203             disp_width = int(disp_height * dev_width / dev_height)
    204             disp_offset_x = 0
    205             disp_offset_y = screen_height - disp_height
    206 
    207         return (disp_width, disp_height, disp_offset_x, disp_offset_y)
    208 
    209     def _touch_input_name_re_str(self):
    210         pattern_str = ('touchpad', 'trackpad')
    211         return '(?:%s)' % '|'.join(pattern_str)
    212 
    213     def get_touch_input_dir(self):
    214         """Get touch device input directory."""
    215         input_root_dir = '/sys/class/input'
    216         input_dirs = glob.glob(os.path.join(input_root_dir, 'input*'))
    217         re_pattern = re.compile(self._touch_input_name_re_str(), re.I)
    218         for input_dir in input_dirs:
    219             filename = os.path.join(input_dir, 'name')
    220             if os.path.isfile(filename):
    221                 with open(filename) as f:
    222                     for line in f:
    223                         if re_pattern.search(line) is not None:
    224                             return input_dir
    225         return None
    226 
    227     def get_firmware_version(self):
    228         """Probe the firmware version."""
    229         input_dir = self.get_touch_input_dir()
    230         device_dir = 'device'
    231 
    232         # Get the re search pattern for firmware_version file name
    233         fw_list = ('firmware', 'fw')
    234         ver_list = ('version', 'id')
    235         sep_list = ('_', '-')
    236         re_str = '%s%s%s' % ('(?:%s)' % '|'.join(fw_list),
    237                              '(?:%s)' % '|'.join(sep_list),
    238                              '(?:%s)' % '|'.join(ver_list))
    239         re_pattern = re.compile(re_str, re.I)
    240 
    241         if input_dir is not None:
    242             device_dir = os.path.join(input_dir, 'device', '*')
    243             for f in glob.glob(device_dir):
    244                 if os.path.isfile(f) and re_pattern.search(f):
    245                     with open (f) as f:
    246                         for line in f:
    247                             return line.strip('\n')
    248         return 'unknown'
    249