blob: 7744e8fafb9da775818ddd24757337133dfc3b26 [file] [log] [blame]
#!/bin/sh
# Copyright 2025 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This isn't the exact copy that will be used in production, but it's better
# than pointing shellcheck at /dev/null.
# shellcheck source=../../../scripts/lib/shflags/shflags
. /usr/share/misc/shflags
# shellcheck source=../common/scripts/chromeos-touch-common.sh
. /opt/google/touch/scripts/chromeos-touch-common.sh
DEFINE_boolean 'recovery' "${FLAGS_FALSE}" "Recovery. Allows for rollback" 'r'
DEFINE_string 'device_path' '' "device path" 'p'
DEFINE_string 'device_pid' '' "device pid" 'i'
CIRQUE_FW_LINK_BASE="cirque_firmware.hex"
CIRQUE_FW_UPDATER="/usr/sbin/cirque_touch_fw_update"
GET_ACTIVE_FWVER="-a"
# Parse command line.
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"
update_firmware() {
# Run the Cirque firmware updater in minijail.
local fw_link="$1"
local dev_path="$2"
local cmd_log=""
if ! cmd_log="$(
minijail0 -u fwupdate-hidraw -g fwupdate-hidraw \
-G -v -P /mnt/empty -b / -b /dev,,1 -b /sys -b /proc/ \
-t --uts -e -l -p -N -n -S \
/opt/google/touch/policies/cirque.update.policy \
"${CIRQUE_FW_UPDATER}" "${fw_link}" "${dev_path}" 2>&1
)"; then
log_msg "error: Cirque firmware updater returned failure:\n${cmd_log}"
return 1
fi
return 0
}
get_fw_version_from_disk() {
# The on-disk FW version is determined by reading the filename which
# is in the format "VID_PID_FWmajor.FWminor.hex". We follow the fw's
# link to the actual file then strip away everything in the FW's
# filename but the FW version.
local fw_link="$1"
local fw_filepath=""
local fw_filename=""
local fw_ver=""
if [ ! -L "${fw_link}" ]; then
return
fi
fw_filepath="$(readlink -f "${fw_link}")"
if [ ! -e "${fw_filepath}" ]; then
return
fi
fw_filename="$(basename "${fw_filepath}")"
if [ "${fw_filename%%_*}" != "0488" ]; then
return
fi
fw_ver="${fw_filename#*_}"
fw_ver="${fw_ver#*_}"
fw_ver="${fw_ver%.*}"
echo "${fw_ver}"
}
get_active_fw_version() {
local dev_path="$1"
local active_fw_ver
if active_fw_ver="$(
minijail0 -u fwupdate-hidraw -g fwupdate-hidraw \
-G -v -P /mnt/empty -b / -b /dev,,1 -b /sys -b /proc/ \
-t --uts -e -l -p -N -n -S \
/opt/google/touch/policies/cirque.query.policy \
"${CIRQUE_FW_UPDATER}" "${GET_ACTIVE_FWVER}" "${dev_path}"
)"; then
echo "${active_fw_ver}"
fi
}
cirque_rebind_driver() {
# Unbind and then bind the driver for this touchpad.
local touch_device_path="$1"
local bus_id=""
local driver_path
driver_path="$(readlink -f "${touch_device_path}"/driver)"
# If the driver is not hid-multitouch, we need
# to rebind the driver to the parent device.
if [ "$(basename "${driver_path}")" = "hid-multitouch" ]; then
bus_id="$(basename "${touch_device_path}")"
else
bus_id="$(readlink -f "${touch_device_path}")"
bus_id="${bus_id%/*}"
driver_path="$(readlink -f "${bus_id}/driver")"
bus_id="$(basename "${bus_id}")"
fi
log_msg \
"Attempting to re-bind Cirque device '${bus_id}' to driver '${driver_path}'"
if ! (echo "${bus_id}" > "${driver_path}/unbind")
then
log_msg "Unable to unbind Cirque device."
fi
local retry_cnt=1
local bind_result=1
if (echo "${bus_id}" > "${driver_path}/bind")
then
bind_result=0
fi
# For some boards, the device will be powered off when the kernel driver
# unbinds. Bind the driver immediately after powering off will fail.
while [ "${bind_result}" -ne "0" ] && [ "${retry_cnt}" -le "4" ]; do
retry_cnt="$((retry_cnt + 1))"
log_msg "Failed to bind driver to Cirque device, will retry."
sleep "${retry_cnt}"
if (echo "${bus_id}" > "${driver_path}/bind")
then
bind_result=0
fi
done
if [ "${bind_result}" -ne "0" ]; then
log_msg "Unable to bind Cirque device back to the driver."
return 1
else
log_msg "Cirque device successfully bound to the driver."
return 0
fi
}
main() {
# 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
local active_fw_ver=""
local new_fw_ver=""
local fw_link="/lib/firmware/${CIRQUE_FW_LINK_BASE}"
local dev_path
dev_path=$(echo "${FLAGS_device_path}"/hidraw/hidraw*)
if [ ! -d "${dev_path}" ]; then
die "Unable to find Cirque device path."
fi
dev_path="/dev/${dev_path##*/}"
if [ ! -c "${dev_path}" ]; then
die "Unable to find Cirque device file."
fi
log_msg "Updating Cirque device with product ID ${FLAGS_device_pid}"
active_fw_ver="$(get_active_fw_version "${dev_path}")"
report_initial_version "${FLAGS_device_path}" "Cirque" "${active_fw_ver}"
# If we cannot determine the active firmware version, force an update.
if [ -z "${active_fw_ver}" ]; then
log_msg "warning: Could not determine the active firmware version."
log_msg "You may need to reboot the computer to reload touchpad driver."
FLAGS_recovery="${FLAGS_TRUE}"
else
log_msg "Active firmware version of the Cirque device: ${active_fw_ver}"
fi
fw_link="$(find_fw_link_path "${CIRQUE_FW_LINK_BASE}" "${FLAGS_device_pid}")"
# Quit if no firmware is found for the specified product ID.
if [ "${fw_link}" = "/lib/firmware/${CIRQUE_FW_LINK_BASE}" ]; then
die "No firmware found for this device."
fi
new_fw_ver="$(get_fw_version_from_disk "${fw_link}")"
# If we cannot determine the new firmware version, we fail the update.
if [ -z "${new_fw_ver}" ]; then
die "Unable to find new Cirque FW version on disk."
fi
log_msg "New firmware version for the Cirque device: ${new_fw_ver}"
# Compare firmware versions.
local active_fw_ver_major="${active_fw_ver%%.*}"
local active_fw_ver_minor="${active_fw_ver##*.}"
local new_fw_ver_major="${new_fw_ver%%.*}"
local new_fw_ver_minor="${new_fw_ver##*.}"
# Log update type.
local update_type
update_type="$(compare_multipart_version \
"${active_fw_ver_major}" "${new_fw_ver_major}" \
"${active_fw_ver_minor}" "${new_fw_ver_minor}")"
log_update_type "${update_type}"
update_needed="$(is_update_needed "${update_type}")"
if [ "${update_needed}" -ne "${FLAGS_TRUE}" ]; then
log_msg \
"Cirque firmware update not required. Current firmware: ${active_fw_ver}"
exit 0
fi
log_msg "Updating Cirque firmware to ${new_fw_ver}"
if ! run_cmd_and_block_powerd update_firmware "${fw_link}" "${dev_path}" ;
then
report_update_result "${FLAGS_device_path}" \
"${REPORT_RESULT_FAILURE}" "${new_fw_ver}"
die "Cirque firmware update failed. Current firmware: ${active_fw_ver}"
fi
# Check if update was successful
active_fw_ver="$(get_active_fw_version "${dev_path}")"
if [ -z "${active_fw_ver}" ] || \
[ "${active_fw_ver%%.*}" -ne "${new_fw_ver%%.*}" ] || \
[ "${active_fw_ver##*.}" -ne "${new_fw_ver##*.}" ]; then
report_update_result "${FLAGS_device_path}" \
"${REPORT_RESULT_FAILURE}" "${new_fw_ver}"
die "Cirque firmware update failed. Current firmware: ${active_fw_ver}"
fi
report_update_result "${FLAGS_device_path}" \
"${REPORT_RESULT_SUCCESS}" "${active_fw_ver}"
log_msg \
"Cirque firmware update successful. Current firmware: ${active_fw_ver}"
cirque_rebind_driver "${FLAGS_device_path}"
exit 0
}
main "$@"