blob: 286a9c6ace2ec9cc6894844d8c2a1328d715ff27 [file] [edit]
#!/bin/sh
#
# Copyright 2019 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
overlay_sh_exist=0
OVERLAY_FILE=/opt/google/touch/scripts/chromeos-etphidiap-overlay.sh
if [ -f "${OVERLAY_FILE}" ]; then
overlay_sh_exist=1
# shellcheck source=/opt/google/touch/scripts/chromeos-etphidiap-overlay.sh
. "${OVERLAY_FILE}"
fi
DEFINE_string 'device' '' "i2c-dev device name e.g. i2c-7" 'd'
DEFINE_string 'device_path' '' "device path in /sys" 'p'
DEFINE_boolean 'recovery' ${FLAGS_FALSE} "Recovery. Allows for rollback" 'r'
# Parse command line.
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
# Actually trigger a firmware update by running the ELAN update tool in minijail
# to limit the syscalls it can access.
update_firmware() {
local fw_link="$1"
local fw_version="$2"
local cmd_log=
local ret=
cmd_log="$(
minijail0 -u fwupdate-i2c -g fwupdate-i2c \
-G -I -N -n -p -v --uts \
-S /opt/google/touch/policies/etphidiap.update.policy -- \
/usr/sbin/etphid_updater -i "${FLAGS_device#i2c-}" -b "${fw_link}" 2>&1
)"
ret="$?"
if [ "${ret}" -ne 0 ]; then
report_update_result "${FLAGS_device_path}" "${REPORT_RESULT_FAILURE}" \
"${fw_version}"
die "Exit status ${ret} updating touchpad firmware: ${cmd_log}"
fi
return "${ret}"
}
update_eeprom_firmware() {
local fw_link="$1"
local fw_version="$2"
local cmd_log=
local ret=
cmd_log="$(
minijail0 -u fwupdate-i2c -g fwupdate-i2c \
-G -I -N -n -p -v --uts \
-S /opt/google/touch/policies/etphidiap.update.policy -- \
/usr/sbin/etphid_updater -i "${FLAGS_device#i2c-}" -E "${fw_link}" 2>&1
)"
ret="$?"
if [ "${ret}" -ne 0 ]; then
report_update_result "${FLAGS_device_path}" "${REPORT_RESULT_FAILURE}" \
"${fw_version}"
die "Exit status ${ret} updating touchpad EEPROM firmware: ${cmd_log}"
fi
return "${ret}"
}
update_firmware_region() {
local region_id="$1"
local cmd_log=
local ret=
cmd_log="$(
minijail0 -u fwupdate-i2c -g fwupdate-i2c \
-G -I -N -n -p -v --uts \
-S /opt/google/touch/policies/etphidiap.update.policy -- \
/usr/sbin/etphid_updater -i "${FLAGS_device#i2c-}" -R "${region_id}" 2>&1
)"
ret="$?"
if [ "${ret}" -ne 0 ]; then
report_update_result "${FLAGS_device_path}" "${REPORT_RESULT_FAILURE}" \
"${region_id}"
die "Exit status ${ret} updating touchpad firmware region: ${cmd_log}"
fi
return "${ret}"
}
# The on-disk firmware version is determined by parsing the filename of the
# actual firmware file passed as $1, after resolving any symlinks. The filename
# should be in the format "elan_0xVER.bin" e.g. "elan_0x0A.bin", or without
# board overlay script file then compatible with elan_i2c driver firmware file
# format "{module_id}_{fw_ver}.bin" e.g "182.0_1.0.bin".
# The 0x prefix is preserved, e.g. if the file is named "elan_0x0A.bin" this
# function will print "0x0A" to stdout, or if the file is named
# "182.0_1.0.bin" the function will print "0x1" to stdout.
get_fw_version_from_disk() {
local fw_link=
local fw_filepath=
local fw_filename=
local fw_ver=
# POSIX allows, but does not require, shift to exit a non-interactive shell.
fw_link="$1"; [ "$#" -gt 0 ] || return; shift
[ -L "${fw_link}" ] || return
fw_filepath="$(realpath -e "${fw_link}")" || return
[ -n "${fw_filepath}" ] || return
fw_filename="$(basename "${fw_filepath}")"
fw_ver="${fw_filename#*_}"
fw_ver="${fw_ver%.*}"
if [ "${overlay_sh_exist}" -eq 0 ]; then
fw_ver="${fw_ver%.*}"
fi
printf '0x%X\n' "${fw_ver}"
}
# Query the touchpad controller for information about its firmware or hardware.
get_active_info() {
local description=
local output=
local ret=
# POSIX allows, but does not require, shift to exit a non-interactive shell.
description="$1"; [ "$#" -gt 0 ] || return; shift
output="$(
minijail0 -u fwupdate-i2c -g fwupdate-i2c \
-G -I -N -n -p -v --uts \
-S /opt/google/touch/policies/etphidiap.query.policy -- \
/usr/sbin/etphid_updater -i "${FLAGS_device#i2c-}" "$@" 2>&1
)"
ret="$?"
if [ "${ret}" -eq 0 ]; then
if [ "${output}" = "-1" ]; then
printf '0xFFFFFFFF\n'
elif [ -z "${output##*-*}" ]; then
printf '%d\n' "${output}"
else
printf '0x%X\n' "0x${output}"
fi
else
echo 1>&2 "Exit status ${ret} retrieving touchpad ${description}: ${output}"
fi
return "${ret}"
}
# Query the touchpad controller for its current firmware version.
get_active_fw_version() {
get_active_info "firmware version" -g
}
# Query the touchpad controller for its hardware revision identifier.
get_device_hwid() {
get_active_info "hardware revision" -w
}
# Query the touchpad controller for its hardware module identifier.
get_device_module_id() {
get_active_info "module ID" -m
}
# Query the touchpad controller for its current EEPROM firmware version.
get_active_eeprom_fw_version() {
get_active_info "eeprom firmware version" -G
}
# Query the touchpad controller for its current region id.
get_device_region_id() {
get_active_info "region ID" -r
}
# Query the System firmware for its current region id.
get_system_region_id() {
local ret=
if [ ! -f "/sys/firmware/vpd/ro/region" ]; then
printf '-1\n'
return
fi
ret="$(cat /sys/firmware/vpd/ro/region)"
ret=$(
case "${ret}" in
("fr") echo "1" ;;
("be") echo "1" ;;
("cz") echo "2" ;;
(*) echo "0" ;;
esac)
printf '%d\n' "${ret}"
}
# Touchpad Firmware Region ID Update
region_id_update() {
local active_fw_ver=
local update_type=
local update_needed=
local system_region_id=
local device_region_id=
local region_id=
local firmware_ver=
log_msg "We are now attempting to update touchpad firmware region."
active_fw_ver="$(get_active_fw_version)"
[ -n "${active_fw_ver}" ] || die \
"Unable to determine active firmware version."
log_msg "Active firmware version: ${active_fw_ver}"
firmware_ver="$((active_fw_ver))"
if [ "${firmware_ver}" -lt "8" ]; then
log_msg "Unable to support the region id update of touchpad firmware."
return 0
fi
system_region_id="$(get_system_region_id)"
if [ "${system_region_id}" -lt "0" ]; then
log_msg "Unable to determine active region id of system firmware."
return 0
fi
log_msg "Active System firmware region id: ${system_region_id}"
device_region_id="$(get_device_region_id)"
region_id="$((device_region_id))"
if [ -z "${device_region_id}" ] || [ "${region_id}" -lt "0" ]; then
log_msg "Unable to determine active region id of touchpad firmware."
return 0
fi
log_msg "Active Touchpad firmware region id: ${region_id}"
if [ "${system_region_id}" -ne "${region_id}" ]; then
log_msg "Updating touchpad firmware region id to ${system_region_id}"
run_cmd_and_block_powerd update_firmware_region "${system_region_id}"
# Check if update was successful
device_region_id="$(get_device_region_id)"
region_id="$((device_region_id))"
update_type="$(compare_multipart_version "${region_id}" \
"${system_region_id}")"
if [ "${update_type}" -ne "${UPDATE_NOT_NEEDED_UP_TO_DATE}" ]; then
report_update_result "${FLAGS_device_path}" "${REPORT_RESULT_FAILURE}" \
"${system_region_id}"
log_msg \ "Touchpad firmware region id update failed. " \
"Current firmware region id: ${region_id}"
else
log_msg "Updating touchpad firmware region id succeeded. " \
"Current firmware region id is now: ${region_id}"
report_update_result "${FLAGS_device_path}" "${REPORT_RESULT_SUCCESS}" \
"${region_id}"
rebind_driver "$(cd -P "${FLAGS_device_path}/../.." && pwd)"
fi
fi
}
assert_is_function_from_overlay() {
local func_name=
# POSIX allows, but does not require, shift to exit a non-interactive shell.
func_name="$1"; [ "$#" -gt 0 ] || return; shift
type "${func_name}" | head -n 1 | grep -q -E ' ?function$' || die \
"No ${func_name}() function is defined, this is a bug in the board overlay."
}
# Touchpad EEPROM Firmware Update
eeprom_update() {
local active_fw_ver=
local new_fw_ver=
local update_type=
local update_needed=
local fw_link=
local chassis_id=$1
local module_id=$2
log_msg "We are now attempting to update EEPROM firmware."
fw_basename="elan_eeprom_${module_id}.0.bin"
fw_link="$(find_fw_link_path "${fw_basename}" "${chassis_id}")"
if [ -z "${fw_link}" ]; then
log_msg "Unable to determine path of on-disk EEPROM firmware."
return 0
fi
log_msg "Attempting to load on-disk EEPROM firmware: ${fw_link}"
new_fw_ver="$(get_fw_version_from_disk "${fw_link}")"
log_msg "On-disk EEPROM firmware version: ${new_fw_ver}"
if [ -z "${new_fw_ver}" ]; then
log_msg "Unable to determine version of on-disk EEPROM firmware."
return 0
fi
active_fw_ver="$(get_active_eeprom_fw_version)"
[ -n "${active_fw_ver}" ] || die \
"Unable to determine active EEPROM firmware version."
log_msg "Active EEPROM firmware version: ${active_fw_ver}"
report_initial_version "${FLAGS_device_path}" \
"Elan Touchpad EEPROM I2C HID" "${active_fw_ver}"
update_type="$(compare_multipart_version "${active_fw_ver}" \
"${new_fw_ver}")"
log_update_type "${update_type}"
update_needed="$(is_update_needed "${update_type}")"
if [ "${update_needed}" -eq "${FLAGS_TRUE}" ]; then
log_msg "Updating EEPROM firmware to ${new_fw_ver}"
chromeos-boot-alert update_touchpad_firmware
run_cmd_and_block_powerd update_eeprom_firmware "${fw_link}" "${new_fw_ver}"
# Check if update was successful
active_fw_ver="$(get_active_eeprom_fw_version)"
update_type="$(compare_multipart_version "${active_fw_ver}" \
"${new_fw_ver}")"
if [ "${update_type}" -ne "${UPDATE_NOT_NEEDED_UP_TO_DATE}" ]; then
report_update_result "${FLAGS_device_path}" "${REPORT_RESULT_FAILURE}" \
"${new_fw_ver}"
die "EEPROM firmware update failed. Current EEPROM firmware version: " \
"${active_fw_ver}"
fi
log_msg \
"Updating EEPROM firmware succeeded. EEPROM firmware version is now: " \
"${active_fw_ver}"
report_update_result "${FLAGS_device_path}" "${REPORT_RESULT_SUCCESS}" \
"${active_fw_ver}"
fi
}
# Touchpad Firmware Update
touchpad_update() {
local active_fw_ver=
local new_fw_ver=
local update_type=
local update_needed=
local fw_link=
local module_id=
local chassis_id=$1
local board_rev=$2
local device_module_id=$3
local device_hwid=$4
module_id="$((device_module_id))"
active_fw_ver="$(get_active_fw_version)"
[ -n "${active_fw_ver}" ] || die \
"Unable to determine active firmware version."
log_msg "Active firmware version: ${active_fw_ver}"
report_initial_version "${FLAGS_device_path}" "Elan Touchpad I2C HID" \
"${active_fw_ver}"
if [ "${overlay_sh_exist}" -ne 0 ]; then
assert_is_function_from_overlay get_fw_basename
fw_basename="$(get_fw_basename "${chassis_id}" "${board_rev}" \
"${device_module_id}" "${device_hwid}" "${active_fw_ver}")"
[ -n "${fw_basename}" ] || die \
"Unable to determine which on-disk firmware to consider flashing."
else
fw_basename="elan_i2c_${module_id}.0.bin"
fi
fw_link="$(find_fw_link_path "${fw_basename}" "${chassis_id}")"
if [ -z "${fw_link}" ]; then
log_msg "Unable to determine path of on-disk firmware."
return 0
fi
log_msg "Attempting to load on-disk firmware: ${fw_link}"
new_fw_ver="$(get_fw_version_from_disk "${fw_link}")"
log_msg "On-disk firmware version: ${new_fw_ver}"
if [ -z "${new_fw_ver}" ]; then
log_msg "Unable to determine version of on-disk firmware."
return 0
fi
if [ "$(( ${active_fw_ver} == 0xFFFFFFFF ))" -gt 0 ]; then
log_msg "Active firmware appears to be corrupt, will update firmware."
update_needed="${FLAGS_TRUE}"
else
update_type="$(compare_multipart_version "${active_fw_ver}" \
"${new_fw_ver}")"
log_update_type "${update_type}"
update_needed="$(is_update_needed "${update_type}")"
fi
if [ "${update_needed}" -eq "${FLAGS_TRUE}" ]; then
log_msg "Updating firmware to ${new_fw_ver}"
chromeos-boot-alert update_touchpad_firmware
run_cmd_and_block_powerd update_firmware "${fw_link}" "${new_fw_ver}"
# Check if update was successful
active_fw_ver="$(get_active_fw_version)"
update_type="$(compare_multipart_version "${active_fw_ver}" \
"${new_fw_ver}")"
if [ "${update_type}" -ne "${UPDATE_NOT_NEEDED_UP_TO_DATE}" ]; then
report_update_result "${FLAGS_device_path}" "${REPORT_RESULT_FAILURE}" \
"${new_fw_ver}"
die "Firmware update failed. Current firmware version: ${active_fw_ver}"
fi
log_msg \
"Updating firmware succeeded. Firmware version is now: ${active_fw_ver}"
report_update_result "${FLAGS_device_path}" "${REPORT_RESULT_SUCCESS}" \
"${active_fw_ver}"
# Rebind the whole I2C adapter. https://issuetracker.google.com/142598087
# Important: This touch device should be the only peripheral on its I2C bus!
rebind_driver "$(cd -P "${FLAGS_device_path}/../.." && pwd)"
fi
}
main() {
local active_fw_ver=
local new_fw_ver=
local update_type=
local update_needed=
local fw_link=
local device_hwid=
local device_module_id=
local chassis_id=
local board_rev=
# This script runs early at bootup, so if the touch driver is mistakenly
# included as a module (as opposed to being compiled directly in) the i2c
# device may not be present yet. Pause long enough for for people to notice
# and fix the kernel config.
check_i2c_chardev_driver
chassis_id="$(get_chassis_id)"
log_msg "Chassis identifier detected as: ${chassis_id}"
board_rev="$(get_platform_ver)"
log_msg "Platform version detected as: ${board_rev}"
device_module_id="$(get_device_module_id)"
[ -n "${device_module_id}" ] || die \
"Unable to determine hardware module of the touchpad device."
log_msg "Hardware module of the touchpad device: ${device_module_id}"
module_id="$((device_module_id))"
device_hwid="$(get_device_hwid)"
[ -n "${device_hwid}" ] || die \
"Unable to determine hardware revision of the touchpad device."
log_msg "Hardware revision of the touchpad device: ${device_hwid}"
touchpad_update "${chassis_id}" "${board_rev}" \
"${device_module_id}" "${device_hwid}"
eeprom_update "${chassis_id}" "${module_id}"
if [ "${module_id}" -eq "286" ]; then
region_id_update
fi
}
main "$@"