util: Add new utility 'vpd_icc' for per-device ICC management

Some devices will have each panel calibrated in factory and should have
device-specific ICC profile. This script helps to store and provision
ICC using VPD as backend storage.

To store:
 vpd_icc [product_id].icc # product_id is panel ID in 8 hex digits

To provision:
 vpd_icc

BUG=b:141297270
TEST=(set) vpd_icc 0080e000.icc
           vpd -l # see 'display_profiles'
     (get) vpd_icc
           ls /var/cache/display_profiles # see 0080e000.icc
BRANCH=none

Change-Id: Ia913594c5fc517ea52632fe3ca6278407cbe6fa8
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vpd/+/2058225
Reviewed-by: Stimim Chen <stimim@chromium.org>
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
Commit-Queue: Agnes Cheng <agnescheng@google.com>
Tested-by: Agnes Cheng <agnescheng@google.com>
Auto-Submit: Agnes Cheng <agnescheng@google.com>
diff --git a/util/vpd_icc b/util/vpd_icc
new file mode 100755
index 0000000..aac0e07
--- /dev/null
+++ b/util/vpd_icc
@@ -0,0 +1,79 @@
+#!/bin/sh
+# Copyright 2020 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.
+#
+# Packs ICC profiles to VPD, or to create local ICC profile files from VPD.
+
+VPD_KEY="display_profiles"
+CACHE_DIR="/var/cache/display_profiles"
+ICC_EXT=".icc"
+
+error() {
+  echo "ERROR: $*" >&2
+}
+
+die() {
+  local msg="${1:-Aborted Execution.}"
+  error "${msg}"
+  exit 1
+}
+
+is_valid_product_id() {
+  local name="$1"
+  if [ "${#name}" = 8 ] && [ "$((0x${name}))" -gt 0 ]; then
+    return 0
+  fi
+  return 1
+}
+
+extract_from_vpd() {
+  local raw_value="$(vpd_get_value "${VPD_KEY}")"
+  if [ -z "${raw_value}" ]; then
+    return
+  fi
+  # raw_value format: product_id:base64(gzip(icc))
+  local product_id="${raw_value%%:*}"
+  local content="${raw_value#*:}"
+  if ! is_valid_product_id "${product_id}"; then
+    return
+  fi
+  local icc_path="${CACHE_DIR}/${product_id}${ICC_EXT}"
+  echo "${content}" | base64 -d | gzip -cd >"${icc_path}" ||
+    rm -f "${icc_path}"
+  if [ -s "${icc_path}" ]; then
+    echo "Exported ICC profile for ${product_id}"
+  fi
+}
+
+import_from_file() {
+  local path="$1"
+  local base="$(basename "${path}")"
+  local product_id="${base%%.*}"
+
+  if ! is_valid_product_id "${product_id}"; then
+    die "Invalid input file name (must be 8-digits hex): ${path}"
+  fi
+  local content="$(gzip -c <"${path}" | base64 -w 0)"
+  if [ -z "${content}" ]; then
+    die "Failed to encode from ${path}."
+  fi
+  local cmd="${VPD_KEY}=${product_id}:${content}"
+  echo "New VPD: ${cmd}"
+  vpd -i RO_VPD -s "${cmd}"
+}
+
+main() {
+  case "$#" in
+    0)
+      extract_from_vpd
+      ;;
+    1)
+      import_from_file "$1"
+      ;;
+    *)
+      die "Usage: $0 [ICCFile]"
+      ;;
+  esac
+}
+main "$@"