Add Goodix touchscreen FW updater script

This CL adds touchscreen firmware updater script for Goodix touch
controller. This script read active firmware information form
/dev/hidrawX and compares it will the firmware on disk, if the product
id is equal and firmware version not equal than it will do firmware
update or roll back otherwise print the firmware info and exit.

CQ-DEPEND=CL:948582
BUG=chromium:815399
TEST=Tested on retrofitted device including a Goodix touchscreen with
i2c-hid driver.

Change-Id: I02af2208df36e51b93e207111e23814a1656f8f9
Reviewed-on: https://chromium-review.googlesource.com/948663
Commit-Ready: Charles Wang <charles.goodix@gmail.com>
Tested-by: Charles Wang <charles.goodix@gmail.com>
Reviewed-by: Jingkui Wang <jkwang@google.com>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/policies/amd64/gdixupdate.query.policy b/policies/amd64/gdixupdate.query.policy
new file mode 100644
index 0000000..ea5e239
--- /dev/null
+++ b/policies/amd64/gdixupdate.query.policy
@@ -0,0 +1,30 @@
+# Copyright 2018 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.
+
+# These seccomp rules specify only the syscalls used by the Goodix FW updater
+# tool.  The updater gdixupdate should only be used through minijail with
+# these rules.
+
+# TODO: replace "ioctl: 1" with arg1 list when seccomp support bytecode filter.
+# Because we encode buffer size in the arg1, so it's hard to list all the arg
+# values use in the updater code, you can fix it in the future.
+# All the values of arg1 are generate by macros HIDIOCSFEATURE(size) and
+# HIDIOCGFEATURE(size) defined at linux/hidraw.h.
+ioctl: 1
+mmap: arg2 == 0x1 || arg2 == 0x3 || arg2 == 0x5
+mprotect: arg2 == 0x0 || arg2 == 0x1
+open: 1
+fstat: 1
+close: 1
+read: 1
+brk: 1
+restart_syscall: 1
+exit: 1
+rt_sigreturn: 1
+write: 1
+exit_group: 1
+arch_prctl: arg0 == 0x1002
+access: 1
+munmap: 1
+execve: 1
diff --git a/policies/amd64/gdixupdate.update.policy b/policies/amd64/gdixupdate.update.policy
new file mode 100644
index 0000000..4306e31
--- /dev/null
+++ b/policies/amd64/gdixupdate.update.policy
@@ -0,0 +1,32 @@
+# Copyright 2018 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.
+
+# These seccomp rules specify only the syscalls used by the Goodix FW updater
+# tool.  The updater gdixupdate should only be used through minijail with
+# these rules.
+
+# TODO: replace "ioctl: 1" with arg1 list when seccomp support bytecode filter.
+# Because we encode buffer size in the arg1, so it's hard to list all the arg
+# values use in the updater code, you can fix it in the future.
+# All the values of arg1 are generate by macros HIDIOCSFEATURE(size) and
+# HIDIOCGFEATURE(size) defined at linux/hidraw.h.
+ioctl: 1
+nanosleep: 1
+mmap: arg2 == 0x1 || arg2 == 0x3 || arg2 == 0x5
+mprotect: arg2 == 0x0 || arg2 == 0x1
+open: 1
+close: 1
+read: 1
+fstat: 1
+brk: 1
+lseek: 1
+restart_syscall: 1
+exit: 1
+write: 1
+rt_sigreturn: 1
+exit_group: 1
+arch_prctl: arg0 == 0x1002
+access: 1
+munmap: 1
+execve: 1
diff --git a/scripts/chromeos-goodix-touch-firmware-update.sh b/scripts/chromeos-goodix-touch-firmware-update.sh
new file mode 100755
index 0000000..08307a7
--- /dev/null
+++ b/scripts/chromeos-goodix-touch-firmware-update.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+
+# Copyright 2018 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.
+
+. /usr/share/misc/shflags
+. /opt/google/touch/scripts/chromeos-touch-common.sh
+
+DEFINE_boolean 'recovery' ${FLAGS_FALSE} "Recovery. Allows for rollback" 'r'
+DEFINE_string 'device' '' "device name" 'd'
+
+FW_LINK_NAME="goodix_firmware.bin"
+FW_LINK_PATH="/lib/firmware/goodix-ts.bin"
+GDIXUPDATE="/usr/sbin/gdixupdate"
+GOODIX_VENDOR_ID="27C6"
+
+# Parse command line.
+FLAGS "$@" || exit 1
+eval set -- "${FLAGS_ARGV}"
+
+update_firmware() {
+  local i
+  local ret
+
+  for i in $(seq 3); do
+    minijail0 -S /opt/google/touch/policies/gdixupdate.update.policy \
+        "${GDIXUPDATE}" -f -d "$1" "$2"
+
+    ret=$?
+    if [ ${ret} -eq 0 ]; then
+      return 0
+    fi
+    log_msg "FW update attempt #${i} failed... retrying."
+  done
+  die "Error updating touch firmware. ${ret}"
+}
+
+get_active_firmware_version() {
+  local touch_device_path="$1"
+  minijail0 -S /opt/google/touch/policies/gdixupdate.query.policy \
+      "${GDIXUPDATE}" -p -d "${touch_device_path}"
+}
+
+compare_fw_versions() {
+  local active_fw_version="$1"
+  local fw_version="$2"
+
+  local fw_version_major=""
+  local fw_version_minor=""
+
+  local active_fw_version_major=""
+  local active_fw_version_minor=""
+
+  active_fw_version_major=${active_fw_version%%.*}
+  active_fw_version_minor=${active_fw_version##*.}
+
+  fw_version_major=${fw_version%%.*}
+  fw_version_minor=${fw_version##*.}
+
+  compare_multipart_version "${active_fw_version_major}" "${fw_version_major}" \
+                            "${active_fw_version_minor}" "${fw_version_minor}"
+}
+
+main() {
+  local touch_device_name="${FLAGS_device}"
+  local touch_device_path=""
+  local active_product_id=""
+  local active_fw_version=""
+  local update_type=""
+  local update_needed="${FLAGS_FALSE}"
+  local product_id=""
+  local fw_link_path=""
+  local fw_path=""
+  local fw_name=""
+
+  if [ -z "${FLAGS_device}" ]; then
+    die "Please specify a device using -d"
+  fi
+
+  # Find the device path if it exists "/dev/hidrawX".
+  touch_device_path="$(find_i2c_hid_device "${touch_device_name##*-}")"
+  log_msg "touch_device_path: '${touch_device_path}'"
+  if [ -z "${touch_device_path}" ]; then
+    die "${touch_device_name} not found on system. Aborting update."
+  fi
+
+  # Find the active fw version and the product ID currently in use.
+  active_product_id="${touch_device_name##*_}"
+  active_fw_version="$(get_active_firmware_version "${touch_device_path}")"
+
+  # Find the fw version and product ID on disk.
+  fw_link_path="$(find_fw_link_path "${FW_LINK_NAME}" "${active_product_id}")"
+  log_msg "Attempting to load FW: '${fw_link_path}'"
+
+  fw_path="$(readlink -f "${fw_link_path}")"
+  if [ -z "${fw_path}" ] || [ ! -e "${fw_path}" ]; then
+    die "No valid firmware for ${FLAGS_device} found."
+  fi
+
+  fw_name="$(basename "${fw_path}" ".bin")"
+
+  product_id=${fw_name%_*}
+  fw_version=${fw_name#"${product_id}_"}
+  # Check to make sure we found the device we're expecting. If the product
+  # IDs don't match, abort immediately to avoid flashing the wrong device.
+  if [ "${product_id}" != "${active_product_id}" ]; then
+    log_msg "Current product id: ${active_product_id}"
+    log_msg "Updater product id: ${product_id}"
+    die "Touch firmware updater: Product ID mismatch!"
+  fi
+
+  # Compare the two versions, and see if an update is needed.
+  log_msg "Product ID: ${active_product_id}"
+  log_msg "Current Firmware: ${active_fw_version}"
+  log_msg "Updater Firmware: ${fw_version}"
+
+  update_type="$(compare_fw_versions "${active_fw_version}" "${fw_version}")"
+  log_update_type "${update_type}"
+  update_needed="$(is_update_needed "${update_type}")"
+
+  if [ "${update_needed}" -eq "${FLAGS_TRUE}" ]; then
+    log_msg "Update FW to ${fw_name}"
+    update_firmware "${touch_device_path}" "${fw_path}"
+
+    # Confirm that the FW was updated by checking the current FW version again.
+    active_fw_version="$(get_active_firmware_version "${touch_device_path}")"
+    update_type="$(compare_fw_versions "${active_fw_version}" "${fw_version}")"
+
+    if [ "${update_type}" -ne "${UPDATE_NOT_NEEDED_UP_TO_DATE}" ]; then
+      die "Firmware update failed. Current Firmware: ${active_fw_version}"
+    fi
+    log_msg "Update FW succeded. Current Firmware: ${active_fw_version}"
+  fi
+  exit 0
+}
+
+main "$@"
diff --git a/scripts/chromeos-touch-update.sh b/scripts/chromeos-touch-update.sh
index b3daa39..3a13cf1 100755
--- a/scripts/chromeos-touch-update.sh
+++ b/scripts/chromeos-touch-update.sh
@@ -11,6 +11,7 @@
 SYNAPTICS_VENDOR_ID="06CB"
 WEIDA_VENDOR_ID="2575"
 GOOGLE_VENDOR_ID="18D1"
+GOODIX_VENDOR_ID="27C6"
 
 restore_power_paths=""
 
@@ -56,6 +57,11 @@
         -p "${i2c_path}" -r || \
       logger -t "${LOGGER_TAG}" "${device_name} firmware update failed."
       ;;
+    "${GOODIX_VENDOR_ID}")
+      /opt/google/touch/scripts/chromeos-goodix-touch-firmware-update.sh \
+        -d "${hidname}" -r || \
+      logger -t "${LOGGER_TAG}" "${device_name} firmware update failed."
+      ;;
     *)
       logger -t "${LOGGER_TAG}" "${device_name}: i2c_hid device with "\
                 "unknown vid '${vendor_id}'"