| # Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Classes to extract information about chromebooks and their touch devices.""" |
| |
| import os |
| import re |
| |
| def EnumerateI2CDevices(remote, path): |
| items = remote.Execute("ls " + path) |
| if items is False: |
| return [] |
| |
| device_regex = "[0-9a-z]+-[0-9a-fA-Z:]+" |
| devices = [] |
| for item in re.finditer(device_regex, items): |
| devices.append(item.group(0)) |
| return devices |
| |
| class DeviceInfo(object): |
| """Describes a chromebook's touch device.""" |
| vendor = None |
| |
| def __init__(self, remote, device_id): |
| self.id = device_id |
| self.path = os.path.join("/sys/bus/i2c/devices", device_id) |
| self.kernel_device = None |
| self.name = str(self.vendor) + " Touch Device" |
| self.hw_version = None |
| self.fw_version = None |
| self.symlink = None |
| |
| self.fw_version = self.ReadDeviceFile( |
| remote, ["fw_version", "firmware_version"]) |
| self.hw_version = self.ReadDeviceFile( |
| remote, ["hw_version", "product_id"]) |
| |
| self.hw_name = self.ReadDeviceFile(remote, "name") |
| |
| input_path = os.path.join(self.path, "input") |
| input_name = remote.Execute("ls {}".format(input_path)) |
| if input_name: |
| if len(input_name.splitlines()) > 1: |
| raise Exception("Multiple kernel devices found") |
| |
| input_id = int(input_name[-1:]) |
| self.kernel_device = "/dev/input/event{}".format(input_id) |
| |
| name_path = os.path.join(input_path, input_name, "name") |
| self.name = remote.SafeExecute("cat {}".format(name_path)) |
| |
| def Refresh(self, remote): |
| self.__init__(remote, self.id) |
| |
| def ReadDeviceFile(self, remote, names): |
| if isinstance(names, str): |
| names = [names] |
| for name in names: |
| path = os.path.join(self.path, name) |
| res = remote.Execute("cat " + path) |
| if res: |
| return res.strip() |
| raise Exception("Cannot read any of the remote files: {}".format(names)) |
| |
| def ForceFirmwareUpdate(self, remote): |
| update_path = os.path.join(self.path, "update_fw") |
| remote.SafeExecute("echo 1 > " + update_path, verbose=True) |
| |
| def __str__(self): |
| return "{} {}/{} /lib/firmware/{} @{}".format( |
| self.name, self.hw_version, self.fw_version, self.symlink, self.path) |
| def __repr__(self): |
| return self.__str__() |
| |
| |
| class AtmelDeviceInfo(DeviceInfo): |
| """Implementation of DeviceInfo for Atmel maXTouch devices.""" |
| vendor = "Atmel" |
| |
| def __init__(self, remote, device_id): |
| DeviceInfo.__init__(self, remote, device_id) |
| |
| if self.hw_name == "atmel_mxt_ts": |
| self.symlink = "maxtouch_ts.fw" |
| elif self.hw_name == "atmel_mxt_tp": |
| self.symlink = "maxtouch_tp.fw" |
| elif "Touchscreen" in self.name: |
| self.symlink = "maxtouch_ts.fw" |
| elif "Touchpad" in self.name: |
| self.symlink = "maxtouch_tp.fw" |
| else: |
| raise Exception("Cannot determine Atmel symlink name") |
| |
| def ForceFirmwareUpdate(self, remote): |
| file_path = os.path.join(self.path, "fw_file") |
| remote.SafeExecute("echo {} > {}".format(self.symlink, file_path), |
| verbose=True) |
| return DeviceInfo.ForceFirmwareUpdate(self, remote) |
| |
| @staticmethod |
| def DiscoverAll(remote): |
| def Discover(driver): |
| devices = EnumerateI2CDevices(remote, "/sys/bus/i2c/drivers/%s/" % driver) |
| return [AtmelDeviceInfo(remote, d) for d in devices] |
| return Discover("atmel_mxt_ts") + Discover("atmel_mxt_tp") |
| |
| |
| class ElanDeviceInfo(DeviceInfo): |
| """Implementation of DeviceInfo for Elan touch devices.""" |
| vendor = "Elan" |
| |
| def __init__(self, remote, device_id, symlink): |
| DeviceInfo.__init__(self, remote, device_id) |
| self.symlink = symlink |
| |
| def Refresh(self, remote): |
| self.__init__(remote, self.id, self.symlink) |
| |
| @staticmethod |
| def DiscoverAll(remote): |
| tp_devices = EnumerateI2CDevices(remote, "/sys/bus/i2c/drivers/elan_i2c/") |
| ts_devices = EnumerateI2CDevices(remote, "/sys/bus/i2c/drivers/elants_i2c/") |
| return ([ElanDeviceInfo(remote, d, "elan_i2c.bin") for d in tp_devices] + |
| [ElanDeviceInfo(remote, d, "elants_i2c.bin") for d in ts_devices]) |
| |
| class CypressDeviceInfo(DeviceInfo): |
| """Implementation of DeviceInfo for Cypress touch devices.""" |
| vendor = "Cypress" |
| |
| def __init__(self, remote, device_id): |
| DeviceInfo.__init__(self, remote, device_id) |
| self.symlink = "cyapa.bin" |
| |
| @staticmethod |
| def DiscoverAll(remote): |
| devices = EnumerateI2CDevices(remote, "/sys/bus/i2c/drivers/cyapa/") |
| return [CypressDeviceInfo(remote, d) for d in devices] |
| |
| |
| class CrOSDeviceInfo(object): |
| """CrOSDeviceInfo extracts device information from chromebook. |
| |
| The information is extracted via an SSH remote connection. It will |
| determine which board the device is running and enumerate all touch |
| devices. |
| """ |
| |
| def __init__(self, remote): |
| self.remote = remote |
| self.ip = remote.ip |
| self.arch = remote.SafeExecute("arch") |
| |
| # read board_variant from device |
| info = self.remote.Read("/etc/lsb-release") |
| board_regex = "CHROMEOS_RELEASE_BOARD=([a-zA-Z0-9_-]*)" |
| self.board_variant = re.search(board_regex, info).group(1) |
| |
| # split into board and variant |
| parts = self.board_variant.split("_") |
| self.board = parts[0] |
| self.variant = parts[1] if len(parts) > 1 else None |
| |
| # discover all touch devices that are present |
| device_list = [] |
| device_list.extend(AtmelDeviceInfo.DiscoverAll(remote)) |
| device_list.extend(ElanDeviceInfo.DiscoverAll(remote)) |
| device_list.extend(CypressDeviceInfo.DiscoverAll(remote)) |
| |
| self.touch_devices = {} |
| for device in device_list: |
| self.touch_devices[device.hw_version] = device |