| #!/bin/sh |
| # Copyright 2015 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' |
| DEFINE_string 'driver_name' '' "Which Elan driver (elan_i2c elants_i2c)" 'n' |
| |
| ELAN_I2C_FW_NAME="elan_i2c" |
| ELAN_I2C_FW_VERSION_SYSFS="firmware_version" |
| ELAN_I2C_PRODUCT_ID_SYSFS="product_id" |
| ELANTS_I2C_FW_NAME="elants_i2c" |
| ELANTS_I2C_FW_VERSION_SYSFS="fw_version" |
| ELANTS_I2C_PRODUCT_ID_SYSFS="hw_version" |
| |
| VPD_CACHE_FILE="/mnt/stateful_partition/unencrypted/cache/vpd/filtered.txt" |
| |
| # Parse command line |
| FLAGS "$@" || exit 1 |
| eval set -- "${FLAGS_ARGV}" |
| |
| # Confirm that a device was specified |
| if [ -z "${FLAGS_device}" ]; then |
| die "Please specify a device using -d" |
| fi |
| |
| # Select the right sysfs entries depending on which Elan driver was selected |
| if [ -z "${FLAGS_driver_name}" ]; then |
| die "Please specify a driver name using -n (elants_i2c or elan_i2c)" |
| elif [ "${FLAGS_driver_name}" = "elan_i2c" ]; then |
| FW_NAME="${ELAN_I2C_FW_NAME}" |
| FW_VERSION_SYSFS="${ELAN_I2C_FW_VERSION_SYSFS}" |
| PRODUCT_ID_SYSFS="${ELAN_I2C_PRODUCT_ID_SYSFS}" |
| elif [ "${FLAGS_driver_name}" = "elants_i2c" ]; then |
| FW_NAME="${ELANTS_I2C_FW_NAME}" |
| FW_VERSION_SYSFS="${ELANTS_I2C_FW_VERSION_SYSFS}" |
| PRODUCT_ID_SYSFS="${ELANTS_I2C_PRODUCT_ID_SYSFS}" |
| else |
| die "This script only supports two drivers (elants_i2c and elan_i2c)" |
| fi |
| |
| compare_fw_versions() { |
| local active_fw_version="$1" |
| local fw_version="$2" |
| |
| # Compare the two version numbers and see which is ahead of the other |
| # Elan FW versions are given as floating point numbers, so to compare |
| # them we must use awk. |
| local min_version="$(echo ${active_fw_version} ${fw_version} | |
| awk '{if ($1 < $2) print $1; else print $2}')" |
| |
| if [ "${active_fw_version}" = "${fw_version}" ]; then |
| echo "${UPDATE_NOT_NEEDED_UP_TO_DATE}" |
| elif [ "${active_fw_version}" = "${min_version}" ]; then |
| echo "${UPDATE_NEEDED_OUT_OF_DATE}" |
| elif [ ${FLAGS_recovery} -eq ${FLAGS_TRUE} ]; then |
| echo "${UPDATE_NEEDED_RECOVERY}" |
| else |
| echo "${UPDATE_NOT_NEEDED_AHEAD_OF_DATE}" |
| fi |
| } |
| |
| correct_reported_product_id() { |
| local reported_product_id="$1" |
| local board="$2" |
| |
| # Normally we simply rely on the reported product ID. Unfortunately, for us |
| # Kip and Kip+ are vulnerable to a bug that's makes them report 255.0. When |
| # we see that value and the device is Kip, we need to differentiate them |
| # and manually set the product ID to recover the touchpad. crosbug.com/p/46266 |
| if [ -n "$(echo ${board} | grep "^kip")" ] && [ "${reported_product_id}" = "255.0" ]; then |
| if [ -e "${VPD_CACHE_FILE}" ]; then |
| local customization_id="$(sed -nre 's/^"customization_id"="(.*)"$/\1/p' \ |
| < ${VPD_CACHE_FILE})" |
| if [ "${customization_id}" = "HP-KIP14" ]; then |
| echo "72.0" |
| else |
| echo "75.0" |
| fi |
| fi |
| |
| # If an elan device is reporting 255.0 and there is only an elan_i2c.bin FW |
| # in /lib/firmware (old-style without a product ID appended to the filename) |
| # then it's reasonable to assume the touchpad got wiped and that we should |
| # try to recover it with whatever the FW there happens to be. To do this, we |
| # can just correct the product_id with whatever is on disk. |
| elif [ "${reported_product_id}" = "255.0" ] && |
| [ -f "/lib/firmware/elan_i2c.bin" ] && |
| [ "$(ls /lib/firmware/elan_i2c*.bin | wc -l)" -eq 1 ]; then |
| local fw_path=$(readlink /lib/firmware/elan_i2c.bin) |
| local fw_filename=${fw_path##*/} |
| local fw_name=${fw_filename%.bin} |
| local product_id=${fw_name%_*} |
| echo "${product_id}" |
| |
| # Otherwise, just rely on the product ID that the device reported |
| else |
| echo "${reported_product_id}" |
| fi |
| } |
| |
| elan_update_firmware() { |
| local touch_device_path="$1" |
| local reported_product_id="$2" |
| local corrected_product_id="$3" |
| local fw_path="$4" |
| |
| if [ "${reported_product_id}" = "${corrected_product_id}" ]; then |
| standard_update_firmware "${touch_device_path}" |
| else |
| log_msg "Warning - The product ID of this device has been corrected (it " \ |
| "reported ${reported_product_id}). Attempting to recover now." |
| local tmp_fw_dir="" |
| local linked_fw_name="" |
| local driver_fw_link="/lib/firmware/${FW_NAME}_${reported_product_id}.bin" |
| if [ ! -f "${driver_fw_link}" ]; then |
| driver_fw_link="/lib/firmware/${FW_NAME}.bin" |
| fi |
| linked_fw_name="$(readlink -f "${driver_fw_link}" | xargs basename)" |
| |
| # First make a temp directory where we can put custom symlinks to the right file |
| tmp_fw_dir=`mktemp -d` |
| |
| # Now the correct FW into the tmp directory and rename to match the symlink |
| cp "${fw_path}" "${tmp_fw_dir}/${linked_fw_name}" |
| |
| # Mount the temporary directory over the existing firmware directory |
| mount --bind "${tmp_fw_dir}" /opt/google/touch/firmware |
| |
| # Now that the symlink is set up, continue as normal |
| standard_update_firmware "${touch_device_path}" |
| |
| # Remove the newly mounted directory to return /lib/firmware to normal |
| umount /opt/google/touch/firmware |
| rm -rf "${tmp_fw_dir}" |
| fi |
| } |
| |
| main() { |
| local touch_device_name="${FLAGS_device}" |
| local touch_device_path="" |
| local update_needed=${FLAGS_FALSE} |
| local reported_product_id="" |
| local active_product_id="" |
| local active_fw_version="" |
| local fw_path="" |
| local fw_link_path="" |
| local fw_filename="" |
| local fw_name="" |
| local update_type="" |
| local product_id="" |
| local fw_version="" |
| local board=`grep CHROMEOS_RELEASE_BOARD= /etc/lsb-release | awk -F = '{print $2}'` |
| log_msg "Board detected as '${board}'" |
| |
| # Find the device in the filesystem. |
| required_sysfs="update_fw ${FW_VERSION_SYSFS} ${PRODUCT_ID_SYSFS}" |
| touch_device_path="$(find_i2c_device_by_name "${touch_device_name}" \ |
| "${required_sysfs}")" |
| if [ -z "${touch_device_path}" ]; then |
| die "${touch_device_name} not found on system. Aborting update." |
| fi |
| |
| # Find the product ID that the touch device is reporting itself as. |
| reported_product_id="$(cat ${touch_device_path}/${PRODUCT_ID_SYSFS})" |
| log_msg "Touch controller reported Product ID: ${reported_product_id}" |
| active_product_id="$(correct_reported_product_id ${reported_product_id} ${board})" |
| if [ -z "${active_product_id}" ]; then |
| log_msg "Unable to determine active product id" |
| die "Aborting. Can not continue safely without knowing active product ID" |
| elif [ "${reported_product_id}" != ${active_product_id} ]; then |
| log_msg "Warning: reported product id ${reported_product_id} was corrected" \ |
| "to ${active_product_id} for this device." |
| fi |
| |
| # Find the FW that the updater is considering flashing on the touch device. |
| fw_link_path="$(find_fw_link_path ${active_product_id} ${FW_NAME}.bin)" |
| fw_path="$(readlink "${fw_link_path}")" |
| log_msg "Attempting to load FW: '${fw_link_path}'" |
| log_msg "(which points to '${fw_path}')" |
| if [ ! -e "${fw_link_path}" ] || [ ! -e "${fw_path}" ]; then |
| die "No valid firmware for ${touch_device_name} found." |
| fi |
| fw_filename=${fw_path##*/} |
| fw_name=${fw_filename%.bin} |
| product_id=${fw_name%_*} |
| fw_version=${fw_name#"${product_id}_"} |
| |
| # Make sure the product ID is what the updater expects. |
| local product_id_matches=${FLAGS_FALSE} |
| log_msg "Hardware product id : ${active_product_id}" |
| log_msg "Updater product id : ${product_id}" |
| if [ "${product_id}" = "${active_product_id}" ]; then |
| product_id_matches=${FLAGS_TRUE} |
| elif [ -n "$(echo ${board} | grep "^blaze")" ] && |
| [ "${FLAGS_driver_name}" = "elants_i2c" ] && |
| [ "${active_product_id}" = "232d" -a "${product_id}" = "280d" ]; then |
| # Hack to support Blaze's touchscreen -- chrome-os-partner:29794 |
| product_id_matches=${FLAGS_TRUE} |
| elif [ -n "$(echo ${board} | grep "^enguarde")" ] && |
| [ "${FLAGS_driver_name}" = "elan_i2c" ] && |
| [ "${active_product_id}" = "57.0" -a "${product_id}" = "69.0" ]; then |
| # Hack to support Enguarde's touchpad -- chrome-os-partner:28165 |
| product_id_matches=${FLAGS_TRUE} |
| fi |
| if [ "${product_id_matches}" -ne ${FLAGS_TRUE} ]; then |
| die "Touch firmware updater: Product ID mismatch!" |
| fi |
| |
| # Check the touch device's FW version and compare to the updater's. |
| active_fw_version="$(get_active_firmware_version_from_sysfs \ |
| "${FW_VERSION_SYSFS}" "${touch_device_path}")" |
| 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 the touch device needs an update, trigger it now and confirm it worked. |
| if [ ${update_needed} -eq ${FLAGS_TRUE} ]; then |
| log_msg "Update FW to ${fw_name}" |
| elan_update_firmware "${touch_device_path}" "${reported_product_id}" \ |
| "${active_product_id}" "${fw_path}" |
| |
| active_fw_version="$(get_active_firmware_version_from_sysfs \ |
| "${FW_VERSION_SYSFS}" "${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}" |
| |
| rebind_driver "${touch_device_path}" |
| fi |
| |
| exit 0 |
| } |
| |
| main "$@" |