AVer firmware updater for VC520

This CL adds AVer firmware updater to handle AV520 camera firmware update
Refer to CL:961624 aver-updater: Add aver-updater firmware ebuild

Change-Id: Id5f7e8ee3d012b0214de82932f3ab8ae2fede6d0
BUG=chromium:846116
TEST=we test our device in developer mode on ASUS chromebox.
Main test is hotplug VC520 to chromebox,
and the firmware automatically download from google local mirror.
Then VC520 update the firmware by aver-updater successfully.
Reviewed-on: https://chromium-review.googlesource.com/1074793
Commit-Ready: Zhongze Hu <frankhu@google.com>
Tested-by: Zhongze Hu <frankhu@google.com>
Reviewed-by: Zhongze Hu <frankhu@google.com>
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..37c23f4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,5 @@
+# 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.
+
+include common.mk
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b961574
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+aver-updater is a utility to upgrade aver camera firmwares.
+
+## Requirements
+The GNU C/C++ library is required.
+
+## Building
+At the top level of the directory.
+```
+$ make
+```
+Alternatively at Chromium OS development environment,
+```
+$ emerge-${BOARD} aver-updater
diff --git a/common.mk b/common.mk
new file mode 100644
index 0000000..26c0940
--- /dev/null
+++ b/common.mk
@@ -0,0 +1,953 @@
+# Copyright (c) 2012 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.
+#
+# If this file is part of another source distribution, it's license may be
+# stored in LICENSE.makefile or LICENSE.common.mk.
+#
+# NOTE NOTE NOTE
+#  The authoritative common.mk is located in:
+#    https://chromium.googlesource.com/chromiumos/platform2/+/master/common-mk
+#  Please make all changes there, then copy into place in other repos.
+# NOTE NOTE NOTE
+#
+# This file provides a common architecture for building C/C++ source trees.
+# It uses recursive makefile inclusion to create a single make process which
+# can be built in the source tree or with the build artifacts placed elsewhere.
+#
+# It is fully parallelizable for all targets, including static archives.
+#
+# To use:
+# 1. Place common.mk in your top source level
+# 2. In your top-level Makefile, place "include common.mk" at the top
+# 3. In all subdirectories, create a 'module.mk' file that starts with:
+#      include common.mk
+#    And then contains the remainder of your targets.
+# 4. All build targets should look like:
+#    relative/path/target: relative/path/obj.o
+#
+# See existing makefiles for rule examples.
+#
+# Exported macros:
+#   - cc_binary, cxx_binary provide standard compilation steps for binaries
+#   - cxx_library, cc_library provide standard compilation steps for
+#     shared objects.
+#   All of the above optionally take an argument for extra flags.
+#   - update_archive creates/updates a given .a target
+#
+# Instead of using the build macros, most users can just use wrapped targets:
+#   - CXX_BINARY, CC_BINARY, CC_STATIC_BINARY, CXX_STATIC_BINARY
+#   - CXX_LIBRARY, CC_LIBRARY, CC_STATIC_LIBRARY, CXX_STATIC_LIBRARY
+#   - E.g., CXX_BINARY(mahbinary): foo.o
+#   - object.depends targets may be used when a prerequisite is required for an
+#     object file. Because object files result in multiple build artifacts to
+#     handle PIC and PIE weirdness. E.g.
+#       foo.o.depends: generated/dbus.h
+#   - TEST(binary) or TEST(CXX_BINARY(binary)) may be used as a prerequisite
+#     for the tests target to trigger an automated test run.
+#   - CLEAN(file_or_dir) dependency can be added to 'clean'.
+#
+# If source code is being generated, rules will need to be registered for
+# compiling the objects.  This can be done by adding one of the following
+# to the Makefile:
+#   - For C source files
+#   $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/b.o,CC,c,CFLAGS))
+#   - For C++ source files
+#   $(eval $(call add_object_rules,sub/dir/gen_a.o sub/dir/b.o,CXX,cc,CXXFLAGS))
+#
+# Exported targets meant to have prerequisites added to:
+#  - all - Your desired targets should be given
+#  - tests - Any TEST(test_binary) targets should be given
+#  - FORCE - force the given target to run regardless of changes
+#            In most cases, using .PHONY is preferred.
+#
+# Possible command line variables:
+#   - COLOR=[0|1] to set ANSI color output (default: 1)
+#   - VERBOSE=[0|1] to hide/show commands (default: 0)
+#   - MODE=[opt|dbg|profiling] (default: opt)
+#          opt - Enable optimizations for release builds
+#          dbg - Turn down optimization for debugging
+#          profiling - Turn off optimization and turn on profiling/coverage
+#                      support.
+#   - ARCH=[x86|arm|supported qemu name] (default: from portage or uname -m)
+#   - SPLITDEBUG=[0|1] splits debug info in target.debug (default: 0)
+#        If NOSTRIP=1, SPLITDEBUG will never strip the final emitted objects.
+#   - NOSTRIP=[0|1] determines if binaries are stripped. (default: 1)
+#        NOSTRIP=0 and MODE=opt will also drop -g from the CFLAGS.
+#   - VALGRIND=[0|1] runs tests under valgrind (default: 0)
+#   - OUT=/path/to/builddir puts all output in given path (default: $PWD)
+#   - VALGRIND_ARGS="" supplies extra memcheck arguments
+#
+# Per-target(-ish) variable:
+#   - NEEDS_ROOT=[0|1] allows a TEST() target to run with root.
+#     Default is 0 unless it is running under QEmu.
+#   - NEEDS_MOUNTS=[0|1] allows a TEST() target running on QEmu to get
+#     setup mounts in the $(SYSROOT)
+#
+# Caveats:
+# - Directories or files with spaces in them DO NOT get along with GNU Make.
+#   If you need them, all uses of dir/notdir/etc will need to have magic
+#   wrappers.  Proceed at risk to your own sanity.
+# - External CXXFLAGS and CFLAGS should be passed via the environment since
+#   this file does not use 'override' to control them.
+# - Our version of GNU Make doesn't seem to support the 'private' variable
+#   annotation, so you can't tag a variable private on a wrapping target.
+
+# Behavior configuration variables
+SPLITDEBUG ?= 0
+NOSTRIP ?= 1
+VALGRIND ?= 0
+COLOR ?= 1
+VERBOSE ?= 0
+MODE ?= opt
+CXXEXCEPTIONS ?= 0
+RUN_TESTS ?= 1
+ARCH ?= $(shell uname -m)
+
+# Put objects in a separate tree based on makefile locations
+# This means you can build a tree without touching it:
+#   make -C $SRCDIR  # will create ./build-$(MODE)
+# Or
+#   make -C $SRCDIR OUT=$PWD
+# This variable is extended on subdir calls and doesn't need to be re-called.
+OUT ?= $(PWD)/
+
+# Make OUT now so we can use realpath.
+$(shell mkdir -p "$(OUT)")
+
+# TODO(wad) Relative paths are resolved against SRC and not the calling dir.
+# Ensure a command-line supplied OUT has a slash
+override OUT := $(realpath $(OUT))/
+
+# SRC is not meant to be set by the end user, but during make call relocation.
+# $(PWD) != $(CURDIR) all the time.
+export SRC ?= $(CURDIR)
+
+# If BASE_VER is not set, read the libchrome revision number from
+# common-mk/BASE_VER file.
+ifeq ($(strip $(BASE_VER)),)
+BASE_VER := $(shell cat $(SRC)/../common-mk/BASE_VER)
+endif
+$(info Using BASE_VER=$(BASE_VER))
+
+# Re-start in the $(OUT) directory if we're not there.
+# We may be invoked using -C or bare and we need to ensure behavior
+# is consistent so we check both PWD vs OUT and PWD vs CURDIR.
+override RELOCATE_BUILD := 0
+ifneq (${PWD}/,${OUT})
+override RELOCATE_BUILD := 1
+endif
+# Make sure we're running with no builtin targets. They cause
+# leakage and mayhem!
+ifneq (${PWD},${CURDIR})
+override RELOCATE_BUILD := 1
+# If we're run from the build dir, don't let it get cleaned up later.
+ifeq (${PWD}/,${OUT})
+$(shell touch "$(PWD)/.dont_delete_on_clean")
+endif
+endif  # ifneq (${PWD},${CURDIR}
+
+# "Relocate" if we need to restart without implicit rules.
+ifeq ($(subst r,,$(MAKEFLAGS)),$(MAKEFLAGS))
+override RELOCATE_BUILD := 1
+endif
+
+ifeq (${RELOCATE_BUILD},1)
+# By default, silence build output. Reused below as well.
+QUIET = @
+ifeq ($(VERBOSE),1)
+  QUIET=
+endif
+
+# This target will override all targets, including prerequisites. To avoid
+# calling $(MAKE) once per prereq on the given CMDGOAL, we guard it with a local
+# variable.
+RUN_ONCE := 0
+MAKECMDGOALS ?= all
+# Keep the rules split as newer make does not allow them to be declared
+# on the same line.  But the way :: rules work, the _all here will also
+# invoke the %:: rule while retaining "_all" as the default.
+_all::
+%::
+	$(if $(filter 0,$(RUN_ONCE)), \
+	  cd "$(OUT)" && \
+	  $(MAKE) -r -I "$(SRC)" -f "$(CURDIR)/Makefile" \
+	    SRC="$(CURDIR)" OUT="$(OUT)" $(foreach g,$(MAKECMDGOALS),"$(g)"),)
+	$(eval RUN_ONCE := 1)
+pass-to-subcall := 1
+endif
+
+ifeq ($(pass-to-subcall),)
+
+# Only call MODULE if we're in a submodule
+MODULES_LIST := $(filter-out Makefile %.d,$(MAKEFILE_LIST))
+ifeq ($(words $(filter-out Makefile common.mk %.d $(SRC)/Makefile \
+                           $(SRC)/common.mk,$(MAKEFILE_LIST))),0)
+
+# All the top-level defines outside of module.mk.
+
+#
+# Helper macros
+#
+
+# Create the directory if it doesn't yet exist.
+define auto_mkdir
+  $(if $(wildcard $(dir $1)),$2,$(QUIET)mkdir -p "$(dir $1)")
+endef
+
+# Creates the actual archive with an index.
+# The target $@ must end with .pic.a or .pie.a.
+define update_archive
+  $(call auto_mkdir,$(TARGET_OR_MEMBER))
+  $(QUIET)# Create the archive in one step to avoid parallel use accessing it
+  $(QUIET)# before all the symbols are present.
+  @$(ECHO) "AR		$(subst \
+$(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o)) \
+-> $(subst $(SRC)/,,$(TARGET_OR_MEMBER))"
+  $(QUIET)$(AR) rcs $(TARGET_OR_MEMBER) \
+          $(subst $(SRC)/,,$(^:.o=$(suffix $(basename $(TARGET_OR_MEMBER))).o))
+endef
+
+# Default compile from objects using pre-requisites but filters out
+# subdirs and .d files.
+define cc_binary
+  $(call COMPILE_BINARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS))
+endef
+
+define cxx_binary
+  $(call COMPILE_BINARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS))
+endef
+
+# Default compile from objects using pre-requisites but filters out
+# subdirs and .d files.
+define cc_library
+  $(call COMPILE_LIBRARY_implementation,CC,$(CFLAGS) $(1),$(EXTRA_FLAGS))
+endef
+define cxx_library
+  $(call COMPILE_LIBRARY_implementation,CXX,$(CXXFLAGS) $(1),$(EXTRA_FLAGS))
+endef
+
+# Deletes files silently if they exist. Meant for use in any local
+# clean targets.
+define silent_rm
+  $(if $(wildcard $(1)),
+  $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANFILE$(COLOR_RESET)		' && \
+    $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \
+    $(RM) $(1) 2>/dev/null) || true,)
+endef
+define silent_rmdir
+  $(if $(wildcard $(1)),
+    $(if $(wildcard $(1)/*),
+  $(QUIET)# $(1) not empty [$(wildcard $(1)/*)]. Not deleting.,
+  $(QUIET)($(ECHO) -n '$(COLOR_RED)CLEANDIR$(COLOR_RESET)		' && \
+    $(ECHO) '$(subst $(OUT)/,,$(wildcard $(1)))' && \
+    $(RMDIR) $(1) 2>/dev/null) || true),)
+endef
+
+#
+# Default variable values
+#
+
+# Only override toolchain vars if they are from make.
+CROSS_COMPILE ?=
+define override_var
+ifneq ($(filter undefined default,$(origin $1)),)
+$1 = $(CROSS_COMPILE)$2
+endif
+endef
+$(eval $(call override_var,AR,ar))
+$(eval $(call override_var,CC,gcc))
+$(eval $(call override_var,CXX,g++))
+$(eval $(call override_var,OBJCOPY,objcopy))
+$(eval $(call override_var,PKG_CONFIG,pkg-config))
+$(eval $(call override_var,RANLIB,ranlib))
+$(eval $(call override_var,STRIP,strip))
+
+RMDIR ?= rmdir
+ECHO = /bin/echo -e
+
+ifeq ($(lastword $(subst /, ,$(CC))),clang)
+CDRIVER = clang
+else
+CDRIVER = gcc
+endif
+
+ifeq ($(lastword $(subst /, ,$(CXX))),clang++)
+CXXDRIVER = clang
+else
+CXXDRIVER = gcc
+endif
+
+# Internal macro to support check_XXX macros below.
+# Usage: $(call check_compile, [code], [compiler], [code_type], [c_flags],
+#               [extra_c_flags], [library_flags], [success_ret], [fail_ret])
+# Return: [success_ret] if compile succeeded, otherwise [fail_ret]
+check_compile = $(shell printf '%b\n' $(1) | \
+  $($(2)) $($(4)) -x $(3) $(LDFLAGS) $(5) - $(6) -o /dev/null > /dev/null 2>&1 \
+  && echo "$(7)" || echo "$(8)")
+
+# Helper macro to check whether a test program will compile with the specified
+# compiler flags.
+# Usage: $(call check_compile_cc, [code], [flags], [alternate_flags])
+# Return: [flags] if compile succeeded, otherwise [alternate_flags]
+check_compile_cc = $(call check_compile,$(1),CC,c,CFLAGS,$(2),,$(2),$(3))
+check_compile_cxx = $(call check_compile,$(1),CXX,c++,CXXFLAGS,$(2),,$(2),$(3))
+
+# Helper macro to check whether a test program will compile with the specified
+# libraries.
+# Usage: $(call check_compile_cc, [code], [library_flags], [alternate_flags])
+# Return: [library_flags] if compile succeeded, otherwise [alternate_flags]
+check_libs_cc = $(call check_compile,$(1),CC,c,CFLAGS,,$(2),$(2),$(3))
+check_libs_cxx = $(call check_compile,$(1),CXX,c++,CXXFLAGS,,$(2),$(2),$(3))
+
+# Helper macro to check whether the compiler accepts the specified flags.
+# Usage: $(call check_compile_cc, [flags], [alternate_flags])
+# Return: [flags] if compile succeeded, otherwise [alternate_flags]
+check_cc = $(call check_compile_cc,'int main() { return 0; }',$(1),$(2))
+check_cxx = $(call check_compile_cxx,'int main() { return 0; }',$(1),$(2))
+
+# Choose the stack protector flags based on whats supported by the compiler.
+SSP_CFLAGS := $(call check_cc,-fstack-protector-strong)
+ifeq ($(SSP_CFLAGS),)
+ SSP_CFLAGS := $(call check_cc,-fstack-protector-all)
+endif
+
+# To update these from an including Makefile:
+#  CXXFLAGS += -mahflag  # Append to the list
+#  CXXFLAGS := -mahflag $(CXXFLAGS) # Prepend to the list
+#  CXXFLAGS := $(filter-out badflag,$(CXXFLAGS)) # Filter out a value
+# The same goes for CFLAGS.
+COMMON_CFLAGS-gcc := -fvisibility=internal -ggdb3 -Wa,--noexecstack
+COMMON_CFLAGS-clang := -fvisibility=hidden -ggdb
+COMMON_CFLAGS := -Wall -Wunused -Wno-unused-parameter -Werror -Wformat=2 \
+  -fno-strict-aliasing $(SSP_CFLAGS) -O1
+CXXFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CXXDRIVER)) -std=gnu++11
+CFLAGS += $(COMMON_CFLAGS) $(COMMON_CFLAGS-$(CDRIVER)) -std=gnu11
+CPPFLAGS += -D_FORTIFY_SOURCE=2
+
+# Enable large file support.
+CPPFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
+
+# Disable exceptions based on the CXXEXCEPTIONS setting.
+ifeq ($(CXXEXCEPTIONS),0)
+  CXXFLAGS := $(CXXFLAGS) -fno-exceptions -fno-unwind-tables \
+    -fno-asynchronous-unwind-tables
+endif
+
+ifeq ($(MODE),opt)
+  # Up the optimizations.
+  CFLAGS := $(filter-out -O1,$(CFLAGS)) -O2
+  CXXFLAGS := $(filter-out -O1,$(CXXFLAGS)) -O2
+  # Only drop -g* if symbols aren't desired.
+  ifeq ($(NOSTRIP),0)
+    # TODO: do we want -fomit-frame-pointer on x86?
+    CFLAGS := $(filter-out -ggdb3,$(CFLAGS))
+    CXXFLAGS := $(filter-out -ggdb3,$(CXXFLAGS))
+  endif
+endif
+
+ifeq ($(MODE),profiling)
+  CFLAGS := $(CFLAGS) -O0 -g  --coverage
+  CXXFLAGS := $(CXXFLAGS) -O0 -g  --coverage
+  LDFLAGS := $(LDFLAGS) --coverage
+endif
+
+LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,noexecstack -Wl,-z,now
+
+# Fancy helpers for color if a prompt is defined
+ifeq ($(COLOR),1)
+COLOR_RESET = \x1b[0m
+COLOR_GREEN = \x1b[32;01m
+COLOR_RED = \x1b[31;01m
+COLOR_YELLOW = \x1b[33;01m
+endif
+
+# By default, silence build output.
+QUIET = @
+ifeq ($(VERBOSE),1)
+  QUIET=
+endif
+
+#
+# Implementation macros for compile helpers above
+#
+
+# Useful for dealing with pie-broken toolchains.
+# Call make with PIE=0 to disable default PIE use.
+OBJ_PIE_FLAG = -fPIE
+COMPILE_PIE_FLAG = -pie
+ifeq ($(PIE),0)
+  OBJ_PIE_FLAG =
+  COMPILE_PIE_FLAG =
+endif
+
+# Favor member targets first for CXX_BINARY(%) magic.
+# And strip out nested members if possible.
+LP := (
+RP := )
+TARGET_OR_MEMBER = $(lastword $(subst $(LP), ,$(subst $(RP),,$(or $%,$@))))
+
+# Default compile from objects using pre-requisites but filters out
+# all non-.o files.
+define COMPILE_BINARY_implementation
+  @$(ECHO) "LD$(1)		$(subst $(PWD)/,,$(TARGET_OR_MEMBER))"
+  $(call auto_mkdir,$(TARGET_OR_MEMBER))
+  $(QUIET)$($(1)) $(COMPILE_PIE_FLAGS) -o $(TARGET_OR_MEMBER) \
+    $(2) $(LDFLAGS) \
+    $(filter %.o %.a,$(^:.o=.pie.o)) \
+    $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \
+                            -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \
+    $(LDLIBS)
+  $(call conditional_strip)
+  @$(ECHO) -n "BIN		"
+  @$(ECHO) "$(COLOR_GREEN)$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)"
+  @$(ECHO) "	$(COLOR_YELLOW)-----$(COLOR_RESET)"
+endef
+
+# TODO: add version support extracted from PV environment variable
+#ifeq ($(PV),9999)
+#$(warning PV=$(PV). If shared object versions matter, please force PV=.)
+#endif
+# Then add -Wl,-soname,$@.$(PV) ?
+
+# Default compile from objects using pre-requisites but filters out
+# all non-.o values. (Remember to add -L$(OUT) -llib)
+COMMA := ,
+define COMPILE_LIBRARY_implementation
+  @$(ECHO) "SHARED$(1)	$(subst $(PWD)/,,$(TARGET_OR_MEMBER))"
+  $(call auto_mkdir,$(TARGET_OR_MEMBER))
+  $(QUIET)$($(1)) -shared -Wl,-E -o $(TARGET_OR_MEMBER) \
+    $(2) $(LDFLAGS) \
+    $(if $(filter %.a,$^),-Wl$(COMMA)--whole-archive,) \
+    $(filter %.o ,$(^:.o=.pic.o)) \
+    $(foreach a,$(filter %.a,$^),-L$(dir $(a)) \
+                            -l$(patsubst lib%,%,$(basename $(notdir $(a))))) \
+    $(foreach so,$(filter %.so,$^),-L$(dir $(so)) \
+                            -l$(patsubst lib%,%,$(basename $(notdir $(so))))) \
+    $(LDLIBS)
+  $(call conditional_strip)
+  @$(ECHO) -n "LIB		$(COLOR_GREEN)"
+  @$(ECHO) "$(subst $(PWD)/,,$(TARGET_OR_MEMBER))$(COLOR_RESET)"
+  @$(ECHO) "	$(COLOR_YELLOW)-----$(COLOR_RESET)"
+endef
+
+define conditional_strip
+  $(if $(filter 0,$(NOSTRIP)),$(call strip_artifact))
+endef
+
+define strip_artifact
+  @$(ECHO) "STRIP		$(subst $(OUT)/,,$(TARGET_OR_MEMBER))"
+  $(if $(filter 1,$(SPLITDEBUG)), @$(ECHO) -n "DEBUG	"; \
+    $(ECHO) "$(COLOR_YELLOW)\
+$(subst $(OUT)/,,$(TARGET_OR_MEMBER)).debug$(COLOR_RESET)")
+  $(if $(filter 1,$(SPLITDEBUG)), \
+    $(QUIET)$(OBJCOPY) --only-keep-debug "$(TARGET_OR_MEMBER)" \
+      "$(TARGET_OR_MEMBER).debug")
+  $(if $(filter-out dbg,$(MODE)),$(QUIET)$(STRIP) --strip-unneeded \
+    "$(TARGET_OR_MEMBER)",)
+endef
+
+#
+# Global pattern rules
+#
+
+# Below, the archive member syntax is abused to create fancier
+# syntactic sugar for recipe authors that avoids needed to know
+# subcall options.  The downside is that make attempts to look
+# into the phony archives for timestamps. This will cause the final
+# target to be rebuilt/linked on _every_ call to make even when nothing
+# has changed.  Until a better way presents itself, we have helpers that
+# do the stat check on make's behalf.  Dodgy but simple.
+define old_or_no_timestamp
+  $(if $(realpath $%),,$(1))
+  $(if $(shell find $^ -cnewer "$%" 2>/dev/null),$(1))
+endef
+
+define check_deps
+  $(if $(filter 0,$(words $^)),\
+    $(error Missing dependencies or declaration of $@($%)),)
+endef
+
+# Build a cxx target magically
+CXX_BINARY(%):
+	$(call check_deps)
+	$(call old_or_no_timestamp,$(call cxx_binary))
+clean: CLEAN(CXX_BINARY*)
+
+CC_BINARY(%):
+	$(call check_deps)
+	$(call old_or_no_timestamp,$(call cc_binary))
+clean: CLEAN(CC_BINARY*)
+
+CXX_STATIC_BINARY(%):
+	$(call check_deps)
+	$(call old_or_no_timestamp,$(call cxx_binary,-static))
+clean: CLEAN(CXX_STATIC_BINARY*)
+
+CC_STATIC_BINARY(%):
+	$(call check_deps)
+	$(call old_or_no_timestamp,$(call cc_binary,-static))
+clean: CLEAN(CC_STATIC_BINARY*)
+
+CXX_LIBRARY(%):
+	$(call check_deps)
+	$(call old_or_no_timestamp,$(call cxx_library))
+clean: CLEAN(CXX_LIBRARY*)
+
+CXX_LIBARY(%):
+	$(error Typo alert! LIBARY != LIBRARY)
+
+CC_LIBRARY(%):
+	$(call check_deps)
+	$(call old_or_no_timestamp,$(call cc_library))
+clean: CLEAN(CC_LIBRARY*)
+
+CC_LIBARY(%):
+	$(error Typo alert! LIBARY != LIBRARY)
+
+CXX_STATIC_LIBRARY(%):
+	$(call check_deps)
+	$(call old_or_no_timestamp,$(call update_archive))
+clean: CLEAN(CXX_STATIC_LIBRARY*)
+
+CXX_STATIC_LIBARY(%):
+	$(error Typo alert! LIBARY != LIBRARY)
+
+CC_STATIC_LIBRARY(%):
+	$(call check_deps)
+	$(call old_or_no_timestamp,$(call update_archive))
+clean: CLEAN(CC_STATIC_LIBRARY*)
+
+CC_STATIC_LIBARY(%):
+	$(error Typo alert! LIBARY != LIBRARY)
+
+
+TEST(%): %
+	$(call TEST_implementation)
+ifneq ($(RUN_TESTS),0)
+TEST(%): qemu_chroot_install
+endif
+.PHONY: TEST
+
+# multiple targets with a wildcard need to share an directory.
+# Don't use this directly it just makes sure the directory is removed _after_
+# the files are.
+CLEANFILE(%):
+	$(call silent_rm,$(TARGET_OR_MEMBER))
+.PHONY: CLEANFILE
+
+CLEAN(%): CLEANFILE(%)
+	$(QUIET)# CLEAN($%) meta-target called
+	$(if $(filter-out $(PWD)/,$(dir $(abspath $(TARGET_OR_MEMBER)))), \
+	  $(call silent_rmdir,$(dir $(abspath $(TARGET_OR_MEMBER)))),\
+	  $(QUIET)# Not deleting $(dir $(abspath $(TARGET_OR_MEMBER))) yet.)
+.PHONY: CLEAN
+
+#
+# Top-level objects and pattern rules
+#
+
+# All objects for .c files at the top level
+C_OBJECTS = $(patsubst $(SRC)/%.c,%.o,$(wildcard $(SRC)/*.c))
+
+
+# All objects for .cxx files at the top level
+CXX_OBJECTS = $(patsubst $(SRC)/%.cc,%.o,$(wildcard $(SRC)/*.cc))
+
+# Note, the catch-all pattern rules don't work in subdirectories because
+# we're building from the $(OUT) directory. At the top-level (here) they will
+# work, but we go ahead and match using the module form.  Then we can place a
+# generic pattern rule to capture leakage from the main Makefile. (Later in the
+# file.)
+#
+# The reason target specific pattern rules work well for modules,
+# MODULE_C_OBJECTS, is because it scopes the behavior to the given target which
+# ensures we get a relative directory offset from $(OUT) which otherwise would
+# not match without further magic on a per-subdirectory basis.
+
+# Creates object file rules. Call with eval.
+# $(1) list of .o files
+# $(2) source type (CC or CXX)
+# $(3) source suffix (cc or c)
+# $(4) compiler flag name (CFLAGS or CXXFLAGS)
+# $(5) source dir: _only_ if $(SRC). Leave blank for obj tree.
+define add_object_rules
+$(patsubst %.o,%.pie.o,$(1)): %.pie.o: $(5)%.$(3) %.o.depends
+	$$(call auto_mkdir,$$@)
+	$$(call OBJECT_PATTERN_implementation,$(2),\
+          $$(basename $$@),$$($(4)) $$(CPPFLAGS) $$(OBJ_PIE_FLAG))
+
+$(patsubst %.o,%.pic.o,$(1)): %.pic.o: $(5)%.$(3) %.o.depends
+	$$(call auto_mkdir,$$@)
+	$$(call OBJECT_PATTERN_implementation,$(2),\
+          $$(basename $$@),$$($(4)) $$(CPPFLAGS) -fPIC)
+
+# Placeholder for depends
+$(patsubst %.o,%.o.depends,$(1)):
+	$$(call auto_mkdir,$$@)
+	$$(QUIET)touch "$$@"
+
+$(1): %.o: %.pic.o %.pie.o
+	$$(call auto_mkdir,$$@)
+	$$(QUIET)touch "$$@"
+endef
+
+define OBJECT_PATTERN_implementation
+  @$(ECHO) "$(1)		$(subst $(SRC)/,,$<) -> $(2).o"
+  $(call auto_mkdir,$@)
+  $(QUIET)$($(1)) -c -MD -MF $(2).d $(3) -o $(2).o $<
+  $(QUIET)# Wrap all the deps in $$(wildcard) so a missing header
+  $(QUIET)# won't cause weirdness.  First we remove newlines and \,
+  $(QUIET)# then wrap it.
+  $(QUIET)sed -i -e :j -e '$$!N;s|\\\s*\n| |;tj' \
+    -e 's|^\(.*\s*:\s*\)\(.*\)$$|\1 $$\(wildcard \2\)|' $(2).d
+endef
+
+# Now actually register handlers for C(XX)_OBJECTS.
+$(eval $(call add_object_rules,$(C_OBJECTS),CC,c,CFLAGS,$(SRC)/))
+$(eval $(call add_object_rules,$(CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/))
+
+# Disable default pattern rules to help avoid leakage.
+# These may already be handled by '-r', but let's keep it to be safe.
+%: %.o ;
+%.a: %.o ;
+%.o: %.c ;
+%.o: %.cc ;
+
+# NOTE: A specific rule for archive objects is avoided because parallel
+#       update of the archive causes build flakiness.
+# Instead, just make the objects the prerequisites and use update_archive
+# To use the foo.a(obj.o) functionality, targets would need to specify the
+# explicit object they expect on the prerequisite line.
+
+#
+# Architecture detection and QEMU wrapping
+#
+
+HOST_ARCH ?= $(shell uname -m)
+override ARCH := $(strip $(ARCH))
+override HOST_ARCH := $(strip $(HOST_ARCH))
+# emake will supply "x86" or "arm" for ARCH, but
+# if uname -m runs and you get x86_64, then this subst
+# will break.
+ifeq ($(subst x86,i386,$(ARCH)),i386)
+  QEMU_ARCH := $(subst x86,i386,$(ARCH))  # x86 -> i386
+else ifeq ($(subst amd64,x86_64,$(ARCH)),x86_64)
+  QEMU_ARCH := $(subst amd64,x86_64,$(ARCH))  # amd64 -> x86_64
+else
+  QEMU_ARCH = $(ARCH)
+endif
+override QEMU_ARCH := $(strip $(QEMU_ARCH))
+
+# If we're cross-compiling, try to use qemu for running the tests.
+ifneq ($(QEMU_ARCH),$(HOST_ARCH))
+  ifeq ($(SYSROOT),)
+    $(info SYSROOT not defined. qemu-based testing disabled)
+  else
+    # A SYSROOT is assumed for QEmu use.
+    USE_QEMU ?= 1
+
+    # Allow 64-bit hosts to run 32-bit without qemu.
+    ifeq ($(HOST_ARCH),x86_64)
+      ifeq ($(QEMU_ARCH),i386)
+        USE_QEMU = 0
+      endif
+    endif
+  endif
+else
+  USE_QEMU ?= 0
+endif
+
+# Normally we don't need to run as root or do bind mounts, so only
+# enable it by default when we're using QEMU.
+NEEDS_ROOT ?= $(USE_QEMU)
+NEEDS_MOUNTS ?= $(USE_QEMU)
+
+SYSROOT_OUT = $(OUT)
+ifneq ($(SYSROOT),)
+  SYSROOT_OUT = $(subst $(SYSROOT),,$(OUT))
+else
+  # Default to / when all the empty-sysroot logic is done.
+  SYSROOT = /
+endif
+
+QEMU_NAME = qemu-$(QEMU_ARCH)
+QEMU_PATH = /build/bin/$(QEMU_NAME)
+QEMU_SYSROOT_PATH = $(SYSROOT)$(QEMU_PATH)
+QEMU_SRC_PATH = /usr/bin/$(QEMU_NAME)
+QEMU_BINFMT_PATH = /proc/sys/fs/binfmt_misc/$(QEMU_NAME)
+QEMU_REGISTER_PATH = /proc/sys/fs/binfmt_misc/register
+
+QEMU_MAGIC_arm = ":$(QEMU_NAME):M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/build/bin/qemu-arm:"
+
+
+#
+# Output full configuration at top level
+#
+
+# Don't show on clean
+ifneq ($(MAKECMDGOALS),clean)
+  $(info build configuration:)
+  $(info - OUT=$(OUT))
+  $(info - SRC=$(SRC))
+  $(info - MODE=$(MODE))
+  $(info - SPLITDEBUG=$(SPLITDEBUG))
+  $(info - NOSTRIP=$(NOSTRIP))
+  $(info - VALGRIND=$(VALGRIND))
+  $(info - COLOR=$(COLOR))
+  $(info - CXXEXCEPTIONS=$(CXXEXCEPTIONS))
+  $(info - ARCH=$(ARCH))
+  $(info - QEMU_ARCH=$(QEMU_ARCH))
+  $(info - USE_QEMU=$(USE_QEMU))
+  $(info - NEEDS_ROOT=$(NEEDS_ROOT))
+  $(info - NEEDS_MOUNTS=$(NEEDS_MOUNTS))
+  $(info - SYSROOT=$(SYSROOT))
+  $(info )
+endif
+
+#
+# Standard targets with detection for when they are improperly configured.
+#
+
+# all does not include tests by default
+all:
+	$(QUIET)(test -z "$^" && \
+	$(ECHO) "You must add your targets as 'all' prerequisites") || true
+	$(QUIET)test -n "$^"
+
+# Builds and runs tests for the target arch
+# Run them in parallel
+# After the test have completed, if profiling, run coverage analysis
+tests:
+ifeq ($(MODE),profiling)
+	@$(ECHO) "COVERAGE [$(COLOR_YELLOW)STARTED$(COLOR_RESET)]"
+	$(QUIET)FILES="";						\
+		for GCNO in `find . -name "*.gcno"`; do			\
+			GCDA="$${GCNO%.gcno}.gcda";			\
+			if [ -e $${GCDA} ]; then			\
+				FILES="$${FILES} $${GCDA}";		\
+			fi						\
+		done;							\
+		if [ -n "$${FILES}" ]; then				\
+			gcov -l $${FILES};				\
+			lcov --capture --directory .			\
+				--output-file=lcov-coverage.info;	\
+			genhtml lcov-coverage.info			\
+				--output-directory lcov-html;		\
+		fi
+	@$(ECHO) "COVERAGE [$(COLOR_YELLOW)FINISHED$(COLOR_RESET)]"
+endif
+# Standard name everyone else uses.
+check: tests
+.PHONY: check tests
+
+qemu_chroot_install:
+ifeq ($(USE_QEMU),1)
+	$(QUIET)$(ECHO) "QEMU   Preparing $(QEMU_NAME)"
+	@# Copying strategy
+	@# Compare /usr/bin/qemu inode to /build/$board/build/bin/qemu, if different
+	@# hard link to a temporary file, then rename temp to target. This should
+	@# ensure that once $QEMU_SYSROOT_PATH exists it will always exist, regardless
+	@# of simultaneous test setups.
+	$(QUIET)if [[ ! -e $(QEMU_SYSROOT_PATH) || \
+	    `stat -c %i $(QEMU_SRC_PATH)` != `stat -c %i $(QEMU_SYSROOT_PATH)` \
+	    ]]; then \
+	  $(ROOT_CMD) ln -Tf $(QEMU_SRC_PATH) $(QEMU_SYSROOT_PATH).$$$$; \
+	  $(ROOT_CMD) mv -Tf $(QEMU_SYSROOT_PATH).$$$$ $(QEMU_SYSROOT_PATH); \
+	fi
+
+	@# Prep the binfmt handler. First mount if needed, then unregister any bad
+	@# mappings and then register our mapping.
+	@# There may still be some race conditions here where one script de-registers
+	@# and another script starts executing before it gets re-registered, however
+	@# it should be rare.
+	-$(QUIET)[[ -e $(QEMU_REGISTER_PATH) ]] || \
+	  $(ROOT_CMD) mount binfmt_misc -t binfmt_misc \
+	    /proc/sys/fs/binfmt_misc
+
+	-$(QUIET)if [[ -e $(QEMU_BINFMT_PATH) && \
+	      `awk '$$1 == "interpreter" {print $$NF}' $(QEMU_BINFMT_PATH)` != \
+	      "$(QEMU_PATH)" ]]; then \
+	  echo -1 | $(ROOT_CMD) tee $(QEMU_BINFMT_PATH) >/dev/null; \
+	fi
+
+	-$(if $(QEMU_MAGIC_$(ARCH)),$(QUIET)[[ -e $(QEMU_BINFMT_PATH) ]] || \
+	  echo $(QEMU_MAGIC_$(ARCH)) | $(ROOT_CMD) tee $(QEMU_REGISTER_PATH) \
+	    >/dev/null)
+endif
+.PHONY: qemu_clean qemu_chroot_install
+
+# TODO(wad) Move to -L $(SYSROOT) and fakechroot when qemu-user
+#           doesn't hang traversing /proc from SYSROOT.
+SUDO_CMD = sudo
+UNSHARE_CMD = unshare
+QEMU_CMD =
+ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),$(SUDO_CMD) , )
+MOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) mount, \#)
+UMOUNT_CMD = $(if $(filter 1,$(NEEDS_MOUNTS)),$(ROOT_CMD) umount, \#)
+QEMU_LDPATH = $(SYSROOT_LDPATH):/lib64:/lib:/usr/lib64:/usr/lib
+ROOT_CMD_LDPATH = $(SYSROOT_LDPATH):$(SYSROOT)/lib64:
+ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/lib:$(SYSROOT)/usr/lib64:
+ROOT_CMD_LDPATH := $(ROOT_CMD_LDPATH):$(SYSROOT)/usr/lib
+ifeq ($(USE_QEMU),1)
+  export QEMU_CMD = \
+   $(SUDO_CMD) chroot $(SYSROOT) $(QEMU_PATH) \
+   -drop-ld-preload \
+   -E LD_LIBRARY_PATH="$(QEMU_LDPATH):$(patsubst $(OUT),,$(LD_DIRS))" \
+   -E HOME="$(HOME)" -E SRC="$(SRC)" --
+  # USE_QEMU conditional function
+  define if_qemu
+    $(1)
+  endef
+else
+  ROOT_CMD = $(if $(filter 1,$(NEEDS_ROOT)),sudo, ) \
+    LD_LIBRARY_PATH="$(ROOT_CMD_LDPATH):$(LD_DIRS)"
+  define if_qemu
+    $(2)
+  endef
+endif
+
+VALGRIND_CMD =
+ifeq ($(VALGRIND),1)
+  VALGRIND_CMD = /usr/bin/valgrind --tool=memcheck $(VALGRIND_ARGS) --
+endif
+
+ifneq ($(RUN_TESTS),0)
+define TEST_implementation
+  $(QUIET)$(call TEST_setup)
+  $(QUIET)$(call TEST_run)
+  $(QUIET)$(call TEST_teardown)
+  $(QUIET)exit $$(cat $(OUT)$(TARGET_OR_MEMBER).status.test)
+endef
+else
+define TEST_implementation
+endef
+endif
+
+define TEST_setup
+  @$(ECHO) -n "TEST		$(TARGET_OR_MEMBER) "
+  @$(ECHO) "[$(COLOR_YELLOW)SETUP$(COLOR_RESET)]"
+  $(QUIET)# Setup a target-specific results file
+  $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).setup.test)
+  $(QUIET)(echo 1 > $(OUT)$(TARGET_OR_MEMBER).status.test)
+  $(QUIET)(echo > $(OUT)$(TARGET_OR_MEMBER).cleanup.test)
+  $(QUIET)# No setup if we are not using QEMU
+  $(QUIET)# TODO(wad) this is racy until we use a vfs namespace
+  $(call if_qemu,\
+    $(QUIET)(echo "mkdir -p '$(SYSROOT)/proc' '$(SYSROOT)/dev' \
+                            '$(SYSROOT)/mnt/host/source'" \
+             >> "$(OUT)$(TARGET_OR_MEMBER).setup.test"))
+  $(call if_qemu,\
+    $(QUIET)(echo "$(MOUNT_CMD) --bind /mnt/host/source \
+             '$(SYSROOT)/mnt/host/source'" \
+             >> "$(OUT)$(TARGET_OR_MEMBER).setup.test"))
+  $(call if_qemu,\
+    $(QUIET)(echo "$(MOUNT_CMD) --bind /proc '$(SYSROOT)/proc'" \
+             >> "$(OUT)$(TARGET_OR_MEMBER).setup.test"))
+  $(call if_qemu,\
+    $(QUIET)(echo "$(MOUNT_CMD) --bind /dev '$(SYSROOT)/dev'" \
+             >> "$(OUT)$(TARGET_OR_MEMBER).setup.test"))
+endef
+
+define TEST_teardown
+  @$(ECHO) -n "TEST		$(TARGET_OR_MEMBER) "
+  @$(ECHO) "[$(COLOR_YELLOW)TEARDOWN$(COLOR_RESET)]"
+  $(call if_qemu, $(QUIET)$(SHELL) "$(OUT)$(TARGET_OR_MEMBER).cleanup.test")
+endef
+
+# Use GTEST_ARGS.[arch] if defined.
+override GTEST_ARGS.real = \
+ $(call if_qemu,$(GTEST_ARGS.qemu.$(QEMU_ARCH)),$(GTEST_ARGS.host.$(HOST_ARCH)))
+
+define TEST_run
+  @$(ECHO) -n "TEST		$(TARGET_OR_MEMBER) "
+  @$(ECHO) "[$(COLOR_GREEN)RUN$(COLOR_RESET)]"
+  $(QUIET)(echo 1 > "$(OUT)$(TARGET_OR_MEMBER).status.test")
+  $(QUIET)(echo $(ROOT_CMD) SRC="$(SRC)" $(QEMU_CMD) $(VALGRIND_CMD) \
+    "$(strip $(call if_qemu, $(SYSROOT_OUT),$(OUT))$(TARGET_OR_MEMBER))" \
+      $(if $(filter-out 0,$(words $(GTEST_ARGS.real))),$(GTEST_ARGS.real),\
+           $(GTEST_ARGS)) >> "$(OUT)$(TARGET_OR_MEMBER).setup.test")
+  -$(QUIET)$(call if_qemu,$(SUDO_CMD) $(UNSHARE_CMD) -m) $(SHELL) \
+      $(OUT)$(TARGET_OR_MEMBER).setup.test \
+  && echo 0 > "$(OUT)$(TARGET_OR_MEMBER).status.test"
+endef
+
+# Recursive list reversal so that we get RMDIR_ON_CLEAN in reverse order.
+define reverse
+$(if $(1),$(call reverse,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1))
+endef
+
+clean: qemu_clean
+clean: CLEAN($(OUT)*.d) CLEAN($(OUT)*.o) CLEAN($(OUT)*.debug)
+clean: CLEAN($(OUT)*.test) CLEAN($(OUT)*.depends)
+clean: CLEAN($(OUT)*.gcno) CLEAN($(OUT)*.gcda) CLEAN($(OUT)*.gcov)
+clean: CLEAN($(OUT)lcov-coverage.info) CLEAN($(OUT)lcov-html)
+
+clean:
+	$(QUIET)# Always delete the containing directory last.
+	$(call silent_rmdir,$(OUT))
+
+FORCE: ;
+# Empty rule for use when no special targets are needed, like large_tests
+NONE:
+
+.PHONY: clean NONE valgrind NONE
+.DEFAULT_GOAL  :=  all
+# Don't let make blow away "intermediates"
+.PRECIOUS: %.pic.o %.pie.o %.a %.pic.a %.pie.a %.test
+
+# Start accruing build info
+OUT_DIRS = $(OUT)
+LD_DIRS = $(OUT)
+SRC_DIRS = $(SRC)
+
+include $(wildcard $(OUT)*.d)
+SUBMODULE_DIRS = $(wildcard $(SRC)/*/module.mk)
+include $(SUBMODULE_DIRS)
+
+
+else  ## In duplicate inclusions of common.mk
+
+# Get the current inclusion directory without a trailing slash
+MODULE := $(patsubst %/,%, \
+           $(dir $(lastword $(filter-out %common.mk,$(MAKEFILE_LIST)))))
+MODULE := $(subst $(SRC)/,,$(MODULE))
+MODULE_NAME := $(subst /,_,$(MODULE))
+#VPATH := $(MODULE):$(VPATH)
+
+
+# Depth first
+$(eval OUT_DIRS += $(OUT)$(MODULE))
+$(eval SRC_DIRS += $(OUT)$(MODULE))
+$(eval LD_DIRS := $(LD_DIRS):$(OUT)$(MODULE))
+
+# Add the defaults from this dir to rm_clean
+clean: CLEAN($(OUT)$(MODULE)/*.d) CLEAN($(OUT)$(MODULE)/*.o)
+clean: CLEAN($(OUT)$(MODULE)/*.debug) CLEAN($(OUT)$(MODULE)/*.test)
+clean: CLEAN($(OUT)$(MODULE)/*.depends)
+clean: CLEAN($(OUT)$(MODULE)/*.gcno) CLEAN($(OUT)$(MODULE)/*.gcda)
+clean: CLEAN($(OUT)$(MODULE)/*.gcov) CLEAN($(OUT)lcov-coverage.info)
+clean: CLEAN($(OUT)lcov-html)
+
+$(info + submodule: $(MODULE_NAME))
+# We must eval otherwise they may be dropped.
+MODULE_C_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.c,$(MODULE)/%.o,\
+  $(wildcard $(SRC)/$(MODULE)/*.c))
+$(eval $(MODULE_NAME)_C_OBJECTS ?= $(MODULE_C_OBJECTS))
+MODULE_CXX_OBJECTS = $(patsubst $(SRC)/$(MODULE)/%.cc,$(MODULE)/%.o,\
+  $(wildcard $(SRC)/$(MODULE)/*.cc))
+$(eval $(MODULE_NAME)_CXX_OBJECTS ?= $(MODULE_CXX_OBJECTS))
+
+# Note, $(MODULE) is implicit in the path to the %.c.
+# See $(C_OBJECTS) for more details.
+# Register rules for the module objects.
+$(eval $(call add_object_rules,$(MODULE_C_OBJECTS),CC,c,CFLAGS,$(SRC)/))
+$(eval $(call add_object_rules,$(MODULE_CXX_OBJECTS),CXX,cc,CXXFLAGS,$(SRC)/))
+
+# Continue recursive inclusion of module.mk files
+SUBMODULE_DIRS = $(wildcard $(SRC)/$(MODULE)/*/module.mk)
+include $(wildcard $(OUT)$(MODULE)/*.d)
+include $(SUBMODULE_DIRS)
+
+endif
+endif  ## pass-to-subcall wrapper for relocating the call directory
diff --git a/conf/99-run-aver-updater.rules b/conf/99-run-aver-updater.rules
new file mode 100644
index 0000000..9470aec
--- /dev/null
+++ b/conf/99-run-aver-updater.rules
@@ -0,0 +1,10 @@
+# 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.
+
+SUBSYSTEM=="video4linux", ACTION=="add", ENV{ID_VENDOR_ID}=="2574", ENV{ID_MODEL_ID}=="09**", \
+OWNER="cfm-firmware-updaters", GROUP="cfm-firmware-updaters", MODE="0664"
+
+ACTION=="add", KERNEL=="hidraw*", SUBSYSTEM=="hidraw", DEVPATH=="*:2574:0901*|*:2574:0910*",  \
+OWNER="cfm-firmware-updaters", GROUP="cfm-firmware-updaters", MODE="0664", \
+RUN+="/sbin/minijail0 -e -i -I -v -r -n -p -u cfm-firmware-updaters -g cfm-firmware-updaters /usr/sbin/aver-updater --update=true"
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 0000000..0c436ff
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,113 @@
+// 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.
+
+#include <memory>
+
+#include <brillo/flag_helper.h>
+#include <brillo/syslog_logging.h>
+#include "video_device.h"
+#include "utilities.h"
+
+namespace {
+const char kAVerCAM520Pid[] = "0x0910";     // It's the CAM520 product id.
+/**
+ * @brief Configures the logging system.
+ * @param log_file Specifies the log file to redirect to or stdout for console
+ * output.
+ */
+void ConfigureLogging(std::string log_file) {
+  if (log_file.empty()) {
+    brillo::InitLog(brillo::InitFlags::kLogToSyslog |
+                    brillo::InitFlags::kLogToStderrIfTty);
+  } else if (log_file == "stdout") {
+    logging::LoggingSettings logging_settings;
+    logging::InitLogging(logging_settings);
+  } else {
+    logging::LoggingSettings logging_settings;
+    logging_settings.logging_dest = logging::LOG_TO_FILE;
+    logging_settings.log_file = log_file.c_str();
+    logging_settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+    logging::InitLogging(logging_settings);
+  }
+}
+}
+
+int main(int argc, char** argv) {
+  DEFINE_bool(update, false, "Perform firmware update.");
+  DEFINE_bool(force, false, "Force firmware update.");
+  DEFINE_bool(device_version, false, "Show firmware version in the device.");
+  DEFINE_bool(image_version, false, "Show firmware version in Chromebox stored position.");
+  DEFINE_bool(lock, true, "Make sure only 1 updater can be run during update");
+  DEFINE_string(log_to, "", "Specify log file to write messages to.");
+  brillo::FlagHelper::Init(argc, argv, "aver-updater");
+
+  ConfigureLogging(FLAGS_log_to);
+
+  if (FLAGS_lock && !LockUpdater()) {
+    LOG(ERROR) << "There is another aver-updater running. Exiting now...";
+    return 0;
+  }
+
+  AverStatus error = AverStatus::NO_ERROR;
+  std::string video_version;
+  std::string video_image_version;
+  std::string version_info;
+
+  std::unique_ptr<VideoDevice> aver_device(new VideoDevice(kAVerCAM520Pid));
+
+  error = aver_device->OpenDevice();
+  if (error != AverStatus::NO_ERROR) {
+    LOG(ERROR) << "Failed to open the device.";
+    return static_cast<int>(error);
+  }
+
+  if (FLAGS_device_version) {
+    std::string device_version;
+    aver_device->GetDeviceVersion(&device_version);
+    if (error != AverStatus::NO_ERROR)
+        return static_cast<int>(error);
+    LOG(INFO) << "Firmware version on the device:" << device_version;
+  }
+
+  if (FLAGS_image_version) {
+    std::string image_version;
+    error = aver_device->GetImageVersion(&image_version);
+    if (error != AverStatus::NO_ERROR)
+      return static_cast<int>(error);
+    LOG(INFO) << "Latest firmware available:" << image_version;
+  }
+
+  if (FLAGS_update) {
+    error = aver_device->IsDeviceUpToDate(FLAGS_force);
+    if (error != AverStatus::NO_ERROR) {
+      if (error == AverStatus::OPEN_FOLDER_PATH_FAILED)
+          LOG(ERROR) << "Failed to open file folder path.";
+      else if (error == AverStatus::DEVICE_NOT_OPEN)
+          LOG(ERROR) << "Failed to open Video Device.";
+      else if (error == AverStatus::IO_CONTROL_OPERATION_FAILED)
+          LOG(ERROR) << "Failed to do ioctl.";
+      else if (error == AverStatus::DIR_CONTENT_ERR)
+          LOG(ERROR) << "Failed to get directory content.";
+      else if (error == AverStatus::FW_ALREADY_UPDATE)
+          LOG(INFO) << "Firmware is up to date.";
+      else
+          LOG(ERROR) << "Unexpected error.";
+      return static_cast<int>(error);
+    }
+
+    error = aver_device->LoadFirmwareToBuffer();
+    if (error != AverStatus::NO_ERROR) {
+      LOG(ERROR) << "Failed to load firmware to the buffer.";
+      return static_cast<int>(error);
+    }
+
+    LOG(INFO) << "Updating device.";
+    error = aver_device->PerformUpdate();
+    if (error == AverStatus::NO_ERROR)
+      LOG(INFO) << "Done. Updated firmware successfully.";
+    else
+      LOG(ERROR) << "Failed to update device.";
+  }
+  return static_cast<int>(error);
+}
\ No newline at end of file
diff --git a/src/module.mk b/src/module.mk
new file mode 100644
index 0000000..701f5d5
--- /dev/null
+++ b/src/module.mk
@@ -0,0 +1,23 @@
+# Copyright 2017 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.
+
+include common.mk
+
+BINARY_NAME := aver-updater
+
+PC_DEPS = libbrillo-$(BASE_VER) libchrome-$(BASE_VER)
+PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS))
+PC_LIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS))
+LDLIBS += $(PC_LIBS)
+
+CPPFLAGS += $(PC_CFLAGS)
+
+all: CXX_BINARY($(BINARY_NAME))
+
+CXX_BINARY($(BINARY_NAME)): \
+	src/main.o \
+	src/utilities.o \
+	src/video_device.o \
+
+clean: CLEAN(CXX_BINARY($(BINARY_NAME)))
diff --git a/src/utilities.cc b/src/utilities.cc
new file mode 100644
index 0000000..e2a5778
--- /dev/null
+++ b/src/utilities.cc
@@ -0,0 +1,148 @@
+// 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.
+
+#include "utilities.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fstream>
+#include <sstream>
+#include <iostream>
+
+#include <base/files/dir_reader_posix.h>
+#include <base/files/file.h>
+#include <base/files/file_util.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+
+namespace {
+constexpr unsigned int kDefaultVersionComponentCount = 4;
+constexpr unsigned int kReceiveDataStartPosition = 3;
+const char kAverLockFile[] = "/tmp/aver-updater.lock";
+
+std::vector<std::string> SplitString(std::string string,
+                                    std::string delimiters) {
+  return base::SplitString(string, delimiters, base::TRIM_WHITESPACE,
+                        base::SPLIT_WANT_NONEMPTY);
+}
+}  // namespace
+
+bool GetDirectoryContents(std::string directory,
+                          std::vector<std::string>* contents) {
+  base::DirReaderPosix reader(directory.c_str());
+  if (!reader.IsValid())
+    return false;
+
+  while (reader.Next())
+    (*contents).push_back(std::string(reader.name()));
+  return true;
+}
+
+bool ReadFirmwareFileToBuffer(base::FilePath file_path, std::vector<char> *buffer) {
+  int64_t size_of_file;
+  if (!base::GetFileSize( file_path, &size_of_file)) {
+    LOG(ERROR) << "Can't get firmware file size.";
+    return false;
+  }
+
+  std::vector<char> buf(size_of_file);
+  if (base::ReadFile(file_path, buf.data(), size_of_file) < 0) {
+    LOG(ERROR) << "Read firmware file to buffer failed.";
+    return false;
+  }
+
+  *buffer = buf;
+  return true;
+}
+
+int CompareVersions(std::string version1, std::string version2) {
+  std::string dev_ver = version1.substr(0, version1.find_first_not_of("0123456789."));
+  std::string img_ver = version2.substr(0, version2.find_first_not_of("0123456789."));
+  std::vector<std::string> first_tokens = SplitString(dev_ver, ".");
+  std::vector<std::string> second_tokens = SplitString(img_ver, ".");
+
+  while (first_tokens.size() < kDefaultVersionComponentCount)
+    first_tokens.push_back("0");
+  while (second_tokens.size() < kDefaultVersionComponentCount)
+    second_tokens.push_back("0");
+
+  for (unsigned int i = 0; i < kDefaultVersionComponentCount; i++) {
+    int first_version = 0;
+    int second_version = 0;
+    if (!(base::StringToInt(first_tokens[i], &first_version))) {
+      if (first_version == 0)
+        return 1;
+    }
+    if (!(base::StringToInt(second_tokens[i], &second_version))) {
+      if (second_version == 0)
+        return 1;
+    }
+    if (first_version > second_version)
+      return 1;
+    if (first_version < second_version)
+      return -1;
+  }
+  return 0;
+}
+
+bool LockUpdater() {
+  int lockfile = open(kAverLockFile, O_WRONLY | O_CREAT, 0664);
+  if (lockfile < 0)
+    return false;
+
+  struct flock fl;
+  fl.l_type = F_WRLCK;
+  fl.l_whence = SEEK_SET;
+  fl.l_start = 0;
+  fl.l_len = 0;
+  // When the process runs, the file is locked.
+  // And release the lock when the process exits.
+  // The next process can open file and lock it again.
+  return fcntl(lockfile, F_SETLK, &fl) == 0;
+}
+
+bool ReadFileContent(std::string filepath, std::string* output) {
+  if (filepath.empty())
+    return false;
+
+  base::File::Info info;
+  base::FilePath input_file_path(filepath.c_str());
+  GetFileInfo(input_file_path, &info);
+  if (info.size <= 0)
+    return false;
+
+  char* buffer = new char[info.size]();
+  bool result = ReadFile(input_file_path, buffer, info.size);
+
+  *output = buffer;
+  delete[] buffer;
+  return result;
+}
+
+bool ConvertHexStringToInt(std::string hex_string, int* output_value) {
+  if (hex_string.empty())
+    return false;
+  std::stringstream string_stream;
+  string_stream << std::hex << hex_string;
+  unsigned int hex_number;
+  string_stream >> hex_number;
+  if (output_value)
+    *output_value = hex_number;
+  return true;
+}
+
+bool VerifyDeviceResponse(std::vector<char> hid_return_msg,
+                                std::string isp_progress_word) {
+  if (hid_return_msg.size() < isp_progress_word.size() + kReceiveDataStartPosition) {
+    LOG(ERROR) << "The hid_return_msg_ size is too small:" << hid_return_msg.size();
+    return false;
+  }
+
+  if (std::equal(isp_progress_word.begin(), isp_progress_word.end(),
+      hid_return_msg.begin() + kReceiveDataStartPosition))
+    return true;
+  else
+    return false;
+}
\ No newline at end of file
diff --git a/src/utilities.h b/src/utilities.h
new file mode 100644
index 0000000..9f13fb6
--- /dev/null
+++ b/src/utilities.h
@@ -0,0 +1,71 @@
+// 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.
+
+#ifndef SRC_UTILITIES_H_
+#define SRC_UTILITIES_H_
+
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+
+/**
+ * @brief Gets contents of the directory.
+ * @param directory The directory path to get contents.
+ * @param contents Ouput vector of the content paths.
+ * @return true if succeeded, false otherwise.
+ */
+bool GetDirectoryContents(std::string directory,
+                          std::vector<std::string>* contents);
+
+/**
+ * @brief Reads binary file content to buffer.
+ * @param filepath Path to the binary file.
+ * @param buffer It's a buffer to fill firmware.
+ * @return buffer from reading. Buffer is empty if read failed.
+ */
+bool ReadFirmwareFileToBuffer(base::FilePath file_path, std::vector<char> *buffer);
+
+/**
+ * @brief Compares 2 version strings in format major.minor.build or major.minor.
+ * @param version1 The version string 1.
+ * @param version2 The version string 2.
+ * @return 0 equal, -1 if version1 < version2 or 1 if version1 > version2.
+ */
+int CompareVersions(std::string version1, std::string version2);
+
+/**
+ * @brief Locks this updater to have only 1 instance running.
+ * @return true if there is no other aver-updater processed running and was
+ * able to lock the lock file, false otherwise.
+ */
+bool LockUpdater();
+
+/**
+ * @brief Reads file contents.
+ * @param filepath The file path to read.
+ * @param output Output string from reading.
+ * @return true if read ok, false otherwise.
+ */
+bool ReadFileContent(std::string filepath, std::string* output);
+
+/**
+ * @brief Converts hex string to int.
+ * @param hex_string The input hex string.
+ * @param output_value The output int value.
+ * @return false if convert failed, true otherwise.
+ */
+bool ConvertHexStringToInt(std::string hex_string, int* output_value);
+
+/**
+ * @brief Verify hid device response.
+ * @param hid_return_msg The msg is from hid device.
+ * @param isp_progress_word Expected ISP progress word.
+ * @return true if succeeded, false otherwise.
+ */
+bool VerifyDeviceResponse(std::vector<char> hid_return_msg,
+                                std::string isp_progress_word);
+
+#endif  // SRC_UTILITIES_H_
\ No newline at end of file
diff --git a/src/video_device.cc b/src/video_device.cc
new file mode 100644
index 0000000..afa9f75
--- /dev/null
+++ b/src/video_device.cc
@@ -0,0 +1,498 @@
+// 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.
+
+#include "video_device.h"
+
+#include <fcntl.h>
+#include <linux/usb/video.h>
+#include <linux/uvcvideo.h>
+#include <sys/ioctl.h>
+#include <thread>
+
+#include <base/files/file_util.h>
+#include "utilities.h"
+
+namespace {
+const char kIspFileStartAck[] = "isp_file_start_ack";
+const char kIspFileEndAck[] = "isp_file_end_ack";
+const char kIspStartAck[] = "isp_start_ack";
+const char kIspFileName[] = "isp.dat";
+
+const char kVideoImagePath[]  = "/lib/firmware/aver/";
+const char kDefaultVideoDeviceMountPoint[] = "/sys/class/video4linux";
+const char kDefaultHidDeviceMountPoint[] = "/sys/class/hidraw";
+const char kAverCAM520FirmwareNum[] = "0.0.0018.";
+const char kAverCAM520Name[] = "CAM520";
+const char kAverVC520Name[] = "VC520";
+
+constexpr unsigned int kAverVendorID = 0x2574;
+constexpr unsigned int kAverCAM520ProductID = 0x0910;
+constexpr unsigned int kAverVC520ProductID = 0x0901;
+
+constexpr unsigned int kMaxDataSize = 512;
+
+constexpr unsigned int kUvcxUcamFwVersion    = 0x14;
+constexpr unsigned int kCustomizedCmdIsp     = 0x10;
+constexpr unsigned int kReportIdCustomizeCmd = 0x08;
+constexpr unsigned int kReportIdCustomizeAck = 0x09;
+
+constexpr unsigned int kDefaultUvcGetLenQueryControlSize = 2;
+constexpr unsigned int kAVerDefaultImageBlockSize = 508;
+constexpr unsigned int kAVerRebootingWaitSecond = 180;
+constexpr unsigned int kAVerReadDeviceVersionRetryIntervalMs = 500;
+constexpr unsigned int kAVerReadDeviceVersionMaxRetry = 3;
+constexpr unsigned int kAVerXuOne = 5;
+
+/**
+ * @brief report (input) (to pc)
+ */
+struct InReport
+{
+  uint8_t id;                         /**< report id */
+  uint8_t dat[512 - sizeof(uint8_t)]; /**< report data */
+};
+} // namespace
+
+
+VideoDevice::VideoDevice(const std::string& pid)
+    : usb_pid_(pid), file_descriptor_(-1), file_descriptor_hid_(-1), is_open_(false) {
+}
+
+VideoDevice::~VideoDevice() {
+  CloseDevice();
+}
+
+std::string VideoDevice::FindDevice() {
+  std::vector<std::string> contents;
+  bool get_ok = GetDirectoryContents(kDefaultVideoDeviceMountPoint, &contents);
+  if (!get_ok)
+    return std::string();
+
+  for (auto const& content : contents) {
+    if (content.compare(".") == 0 || content.compare("..") == 0)
+      continue;
+
+    std::string video_dir =
+        std::string(kDefaultVideoDeviceMountPoint) + "/" + content;
+
+    std::string name_path = video_dir + "/name";
+    std::string name;
+
+    base::FilePath input_file_path(name_path.c_str());
+    base::ReadFileToString(input_file_path, &name);
+
+    if (name.find(kAverVC520Name) != name.npos ||
+        name.find(kAverCAM520Name) != name.npos)
+      return "/dev/" + content;
+  }
+  return std::string();
+}
+
+std::string VideoDevice::FindHidDevice() {
+  std::vector<std::string> contents;
+  bool get_ok = GetDirectoryContents(kDefaultHidDeviceMountPoint, &contents);
+  if (!get_ok)
+    return std::string();
+
+  for (auto const& content : contents) {
+    if (content.compare(".") == 0 || content.compare("..") == 0)
+      continue;
+
+    std::string hid_dir =
+        std::string(kDefaultHidDeviceMountPoint) + "/" + content;
+
+    std::string vid_path = hid_dir + "/device/../../idVendor";
+    std::string vid;
+
+    std::string pid_path = hid_dir + "/device/../../idProduct";
+    std::string pid;
+
+    if (!ReadFileContent(vid_path, &vid))
+      continue;
+
+    int vidnum = 0;
+    if (!ConvertHexStringToInt(vid, &vidnum))
+      continue;
+
+    if (!ReadFileContent(pid_path, &pid))
+      continue;
+
+    int pidnum = 0;
+    if (!ConvertHexStringToInt(pid, &pidnum))
+      continue;
+
+    if (vidnum == kAverVendorID &&
+       (pidnum == kAverCAM520ProductID || pidnum == kAverVC520ProductID))
+      return "/dev/" + content;
+
+  }
+
+  return std::string();
+}
+
+AverStatus VideoDevice::OpenDevice() {
+  std::string dev_path = FindDevice();
+  if (dev_path.empty())
+    return AverStatus::USB_PID_NOT_FOUND;
+
+  int fd = open(dev_path.c_str(), O_RDWR, 0);
+  if (fd == -1) {
+    PLOG(ERROR) << "Open video fd fail";
+    return AverStatus::OPEN_DEVICE_FAILED;
+  }
+  file_descriptor_ = fd;
+
+  std::string hid_dev_path = FindHidDevice();
+  if (hid_dev_path.empty())
+    return AverStatus::USB_HID_NOT_FOUND;
+
+  int fd_hid = open(hid_dev_path.c_str(), O_RDWR);
+  if (fd_hid == -1) {
+    PLOG(ERROR) << "Open hid fd fail";
+    return AverStatus::OPEN_HID_DEVICE_FAILED;
+  }
+  file_descriptor_hid_ = fd_hid;
+
+  is_open_ = true;
+  return AverStatus::NO_ERROR;
+}
+
+void VideoDevice::CloseDevice() {
+  if (file_descriptor_ >= 0)
+    close(file_descriptor_);
+
+  if (file_descriptor_hid_ >= 0)
+    close(file_descriptor_hid_);
+
+  file_descriptor_ = -1;
+  file_descriptor_hid_ = -1;
+  is_open_ = false;
+}
+
+AverStatus VideoDevice::ReadDeviceVersion(std::string* device_version) {
+  AverStatus error = GetXuControl(kAVerXuOne, kUvcxUcamFwVersion, device_version);
+  if (error != AverStatus::NO_ERROR)
+    return error;
+
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::GetImageVersion(std::string* fw_version) {
+  if (!image_version_.empty()) {
+    *fw_version = image_version_;
+    return AverStatus::NO_ERROR;
+  }
+
+  std::vector<std::string> files;
+  bool get_ok = GetDirectoryContents(kVideoImagePath, &files);
+  if (!get_ok)
+    return AverStatus::DIR_CONTENT_ERR;
+
+  auto selected_file_it = files.end();
+  for (auto it = files.begin(); it != files.end(); it++) {
+    if (it->substr(0, sizeof(kAverCAM520FirmwareNum) - 1) != kAverCAM520FirmwareNum) {
+      continue;
+    }
+
+    if (selected_file_it == files.end() || *selected_file_it < *it) {
+      selected_file_it = it;
+    }
+  }
+
+  if (selected_file_it == files.end())
+    return AverStatus::DIR_CONTENT_ERR;
+
+  *fw_version = image_version_ = *selected_file_it;
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::PerformUpdate() {
+  AverStatus error = IspStatusGet();
+  if (error != AverStatus::NO_ERROR)
+    return error;
+
+  error = IspFileStart();
+  if (error != AverStatus::NO_ERROR)
+    return error;
+
+  error = IspFileDownload(firmware_buffer_, firmware_buffer_.size());
+  if (error != AverStatus::NO_ERROR)
+    return error;
+
+  error = IspFileEnd(firmware_buffer_.size());
+  if (error != AverStatus::NO_ERROR)
+    return error;
+
+  error = IspStart();
+  if (error != AverStatus::NO_ERROR)
+    return error;
+
+  LOG(INFO) << "Isp updating! PLEASE WAIT CAM520 REBOOT.";
+  std::this_thread::sleep_for(std::chrono::seconds(kAVerRebootingWaitSecond));
+  LOG(INFO) << "Isp update finish. CAM520 will reboot!";
+
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::GetXuControl(unsigned char unit_id,
+                              unsigned char control_selector,
+                              std::string* data) {
+  int data_len;
+  AverStatus error = QueryDataSize(unit_id, control_selector, &data_len);
+  if (error != AverStatus::NO_ERROR) {
+    LOG(ERROR) << "Query data size failed.";
+    return error;
+  }
+
+  uint8_t query_data[data_len];
+  memset(query_data, '\0', sizeof(query_data));
+
+  struct uvc_xu_control_query control_query;
+  control_query.unit = unit_id;
+  control_query.selector = control_selector;
+  control_query.query = UVC_GET_CUR;
+  control_query.size = data_len;
+  control_query.data = query_data;
+  int err = HANDLE_EINTR(ioctl(file_descriptor_, UVCIOC_CTRL_QUERY, &control_query));
+  if (err < 0) {
+    LOG(ERROR) << "Uvc ioctl query failed.";
+    return AverStatus::IO_CONTROL_OPERATION_FAILED;
+  }
+
+  for (int i = 0; i < data_len; i++)
+    data->push_back(query_data[i]);
+
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::SendHidControl(const struct OutReport &customize_cmd) {
+  struct InReport *hid_info;
+
+  CHECK(is_open_) << "Attempt to send HID command while the device is closed.";
+
+  int rt = HANDLE_EINTR(write(file_descriptor_hid_,
+                              &customize_cmd, kMaxDataSize - 1));
+  if (rt < 0) {
+      LOG(ERROR) << "Write hid cmd failed.";
+      return AverStatus::WRITE_DEVICE_FAILED;
+  }
+
+  char hid_read_buf[kMaxDataSize];
+  rt = HANDLE_EINTR(read(file_descriptor_hid_, hid_read_buf, kMaxDataSize));
+  if (rt < 0) {
+      LOG(ERROR) << "Read hid msg failed.";
+      return AverStatus::READ_DEVICE_FAILED;
+  }
+
+  hid_info = reinterpret_cast<struct InReport*>(&hid_read_buf);
+  if (hid_info->id != kReportIdCustomizeAck) {
+    LOG(ERROR) << "Read hid msg failed";
+    return AverStatus::READ_DEVICE_FAILED;
+  }
+
+  hid_return_msg_.clear();
+  if (customize_cmd.isp_cmd != UVCX_UCAM_ISP_STATUS ||
+      customize_cmd.isp_cmd != UVCX_UCAM_ISP_FILE_DNLOAD) {
+    for (int i = 0; i < kMaxDataSize; i++)
+      hid_return_msg_.push_back(hid_read_buf[i]);
+  }
+
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::QueryDataSize(unsigned char unit_id,
+                               unsigned char control_selector,
+                               int* data_size) {
+  uint8_t size_data[kDefaultUvcGetLenQueryControlSize];
+  struct uvc_xu_control_query size_query;
+  size_query.unit = unit_id;
+  size_query.selector = control_selector;
+  size_query.query = UVC_GET_LEN;
+  size_query.size = kDefaultUvcGetLenQueryControlSize;
+  size_query.data = size_data;
+  int error = HANDLE_EINTR(ioctl(file_descriptor_, UVCIOC_CTRL_QUERY, &size_query));
+  if (error < 0)
+    return AverStatus::IO_CONTROL_OPERATION_FAILED;
+
+  int size = (size_data[1] << 8) | (size_data[0]);
+  *data_size = size;
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::IsDeviceUpToDate(bool force) {
+  AverStatus error = GetDeviceVersion(&device_version_);
+  if (error != AverStatus::NO_ERROR)
+    return error;
+
+  LOG(INFO) << "Firmware version on the device:" << device_version_;
+
+  error = GetImageVersion(&image_version_);
+  if (error != AverStatus::NO_ERROR)
+    return error;
+
+  LOG(INFO) << "Latest firmware available:" << image_version_;
+
+  if (force)
+    return AverStatus::NO_ERROR;
+
+  if (CompareVersions(device_version_, image_version_) < 0)
+    return AverStatus::NO_ERROR;
+
+  return AverStatus::FW_ALREADY_UPDATE;
+}
+
+AverStatus VideoDevice::GetDeviceVersion(std::string* device_version) {
+  AverStatus error = AverStatus::UNKNOWN;
+  std::string version;
+
+  int attempts_count = 1;
+  while (true) {
+    error = ReadDeviceVersion(&version);
+    if (error == AverStatus::NO_ERROR ||
+        attempts_count > kAVerReadDeviceVersionMaxRetry) {
+      break;
+    }
+
+    LOG(ERROR) << "Failed to read device version, will retry.";
+    attempts_count += 1;
+    std::this_thread::sleep_for(
+      std::chrono::milliseconds(kAVerReadDeviceVersionRetryIntervalMs));
+  }
+
+  if (error != AverStatus::NO_ERROR) {
+    LOG(ERROR) << "Failed to read device version after " <<
+                   kAVerReadDeviceVersionMaxRetry << " retries";
+    return error;
+  }
+
+  *device_version = version;
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::IspStatusGet() {
+  OutReport customize_cmd;
+  memset(&customize_cmd, 0x0, sizeof(customize_cmd));
+  customize_cmd.id = kReportIdCustomizeCmd;
+  customize_cmd.report_cmd = kCustomizedCmdIsp;
+  customize_cmd.isp_cmd = UVCX_UCAM_ISP_STATUS;
+  AverStatus error = SendHidControl(customize_cmd);
+  if (error != AverStatus::NO_ERROR)
+    return error;
+
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::IspFileStart() {
+  OutReport customize_cmd;
+  memset(&customize_cmd, 0x0, sizeof(customize_cmd));
+  customize_cmd.id = kReportIdCustomizeCmd;
+  customize_cmd.report_cmd = kCustomizedCmdIsp;
+  customize_cmd.isp_cmd = UVCX_UCAM_ISP_FILE_START;
+  memcpy(customize_cmd.dat, kIspFileName, sizeof(kIspFileName));
+
+  AverStatus error = SendHidControl(customize_cmd);
+  if (error != AverStatus::NO_ERROR) {
+    LOG(ERROR) << "Failed to send isp file start cmd in CAM520. Error code: "
+               << error;
+    return error;
+  }
+
+  bool ok = VerifyDeviceResponse(hid_return_msg_, kIspFileStartAck);
+  if (!ok) {
+    LOG(ERROR) << "Failed to start isp upload follow in CAM520.";
+    return AverStatus::ISP_FILE_START_HID_CMD_COMPARE_FAILED;
+  }
+
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::IspFileDownload(std::vector<char> buffer, uint32_t isp_file_size) {
+  OutReport customize_cmd;
+  uint32_t offset = 0;
+  while (isp_file_size > 0) {
+    unsigned int block_size =
+        std::min<unsigned int>(isp_file_size, kAVerDefaultImageBlockSize);
+
+    memset(&customize_cmd, 0x0, sizeof(customize_cmd));
+    customize_cmd.id = kReportIdCustomizeCmd;
+    customize_cmd.report_cmd = kCustomizedCmdIsp;
+    customize_cmd.isp_cmd = UVCX_UCAM_ISP_FILE_DNLOAD;
+    std::copy(buffer.cbegin() + offset, buffer.cbegin() + offset + block_size, customize_cmd.dat);
+    AverStatus error = SendHidControl(customize_cmd);
+    if (error != AverStatus::NO_ERROR) {
+      LOG(ERROR) << "Failed to send isp file to CAM520.";
+      return error;
+    }
+
+    offset += block_size;
+    isp_file_size -= block_size;
+  }
+
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::IspFileEnd(uint32_t isp_file_size) {
+  OutReport customize_cmd;
+  memset(&customize_cmd, 0x0, sizeof(customize_cmd));
+  customize_cmd.id = kReportIdCustomizeCmd;
+  customize_cmd.report_cmd = kCustomizedCmdIsp;
+  customize_cmd.isp_cmd = UVCX_UCAM_ISP_FILE_END;
+  memcpy(customize_cmd.dat, kIspFileName, sizeof(kIspFileName));
+  customize_cmd.dat[51] = 1;
+  customize_cmd.dat[52] = static_cast<uint8_t>((isp_file_size & 0x000000FF) >> 0);
+  customize_cmd.dat[53] = static_cast<uint8_t>((isp_file_size & 0x0000FF00) >> 8);
+  customize_cmd.dat[54] = static_cast<uint8_t>((isp_file_size & 0x00FF0000) >> 16);
+  customize_cmd.dat[55] = static_cast<uint8_t>((isp_file_size & 0xFF000000) >> 24);
+  AverStatus error = SendHidControl(customize_cmd);
+  if (error != AverStatus::NO_ERROR) {
+    LOG(ERROR) << "Failed to send isp file end cmd in CAM520. Error code: "
+               << error;
+    return error;
+  }
+
+  bool ok = VerifyDeviceResponse(hid_return_msg_, kIspFileEndAck);
+  if (!ok) {
+    LOG(ERROR) << "Failed to end isp file upload follow in CAM520.";
+    return AverStatus::ISP_FILE_END_HID_CMD_COMPARE_FAILED;
+  }
+
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::IspStart() {
+  OutReport customize_cmd;
+  memset(&customize_cmd, 0x0, sizeof(customize_cmd));
+  customize_cmd.id = kReportIdCustomizeCmd;
+  customize_cmd.report_cmd = kCustomizedCmdIsp;
+  customize_cmd.isp_cmd = UVCX_UCAM_ISP_START;
+  AverStatus error = SendHidControl(customize_cmd);
+  if (error != AverStatus::NO_ERROR) {
+      LOG(ERROR) << "Failed to start isp update follow in CAM520.";
+      return error;
+  }
+
+  bool ok = VerifyDeviceResponse(hid_return_msg_, kIspStartAck);
+  if (!ok) {
+    LOG(ERROR) << "Failed to start isp update follow in CAM520.";
+    return AverStatus::ISP_START_HID_CMD_COMPARE_FAILED;
+  }
+
+  return AverStatus::NO_ERROR;
+}
+
+AverStatus VideoDevice::LoadFirmwareToBuffer(){
+  std::string fw_num;
+  AverStatus error = GetImageVersion(&fw_num);
+  if (error != AverStatus::NO_ERROR)
+    return error;
+
+  std::string file_path = kVideoImagePath;
+  file_path += fw_num;
+  base::FilePath input_file_path(file_path.c_str());
+  std::vector<char> buffer;
+  if (!ReadFirmwareFileToBuffer(input_file_path, &firmware_buffer_))
+    return AverStatus::READ_FW_TO_BUF_FAILED;
+
+  return AverStatus::NO_ERROR;
+}
\ No newline at end of file
diff --git a/src/video_device.h b/src/video_device.h
new file mode 100644
index 0000000..77fe98d
--- /dev/null
+++ b/src/video_device.h
@@ -0,0 +1,222 @@
+// 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.
+
+#ifndef SRC_VIDEO_DEVICE_H_
+#define SRC_VIDEO_DEVICE_H_
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+/**
+ * @brief AVer extension unit command.
+ * ISP means In-system programming.
+ */
+enum AverIspStatus {
+    UVCX_UCAM_ISP_STATUS = 0x01, // Get isp status from AVer device .
+    UVCX_UCAM_ISP_FILE_START,    // Notify AVer device to start isp download follow.
+    UVCX_UCAM_ISP_FILE_DNLOAD,   // Notify AVer device to ready to receive isp file data.
+    UVCX_UCAM_ISP_FILE_END,      // Notify AVer device to end up file download.
+    UVCX_UCAM_ISP_START,         // Notify AVer device to start isp follow.
+};
+
+/**
+ * @brief AVer error report.
+ */
+enum class AverStatus {
+  NO_ERROR = 0,
+  USB_PID_NOT_FOUND,
+  USB_HID_NOT_FOUND,
+  DEVICE_NOT_OPEN,
+  OPEN_DEVICE_FAILED,
+  OPEN_HID_DEVICE_FAILED,
+  READ_DEVICE_FAILED,
+  WRITE_DEVICE_FAILED,
+  INVALID_DEVICE_VERSION_DATA_SIZE,
+  XU_UNIT_ID_INVALID,
+  IO_CONTROL_OPERATION_FAILED,
+  OPEN_FOLDER_PATH_FAILED,
+  DIR_CONTENT_ERR,
+  FW_ALREADY_UPDATE,
+  ISP_START_FAILED,
+  ISP_FILE_START_HID_CMD_COMPARE_FAILED,
+  ISP_FILE_END_HID_CMD_COMPARE_FAILED,
+  ISP_START_HID_CMD_COMPARE_FAILED,
+  READ_FW_TO_BUF_FAILED,
+  UNKNOWN
+};
+
+/**
+ * @brief report (output) (from pc)
+ */
+struct OutReport
+{
+  uint8_t id;                         /**< report id */
+  uint8_t report_cmd;                 /**< report cmd */
+  uint8_t isp_cmd;                    /**< isp cmd */
+  uint8_t dat[508];                   /**< report data */
+};
+
+/**
+ * AVer video device class to handle video firmware update.
+ */
+class VideoDevice final {
+ public:
+
+  /**
+   * @brief Constructor with product id.
+   * @param pid Product id string.
+   */
+  VideoDevice(const std::string& pid);
+
+  ~VideoDevice();
+
+  /**
+   * @brief Opens the device.
+   * @return NO_ERROR if opened ok, error code otherwise.
+   */
+  AverStatus OpenDevice();
+
+  /**
+   * @brief Closes the device.
+   */
+  void CloseDevice();
+
+  /**
+   * @brief Performs firmware update.
+   * @return NO_ERROR if updated ok, error code otherwise.
+   */
+  AverStatus PerformUpdate();
+
+  /**
+   * @brief Gets the device version.
+   * @param device_version Output device version string.
+   * @return NO_ERROR if failed, error code otherwise.
+   */
+  AverStatus GetDeviceVersion(std::string* device_version);
+
+  /**
+   * @brief Checks if device firmware is up to date.
+   * @param force Force the device to do firmware update.
+   * @return NO_ERROR if succeeded, error code otherwise.
+   */
+  AverStatus IsDeviceUpToDate(bool force);
+
+  /**
+   * @brief Load firmware to the buffer.
+   * @return NO_ERROR if succeeded, error code otherwise.
+   */
+  AverStatus LoadFirmwareToBuffer();
+
+  /**
+   * @brief Gets the image version.
+   * @param image_version_ Output device version string.
+   * @return NO_ERROR if succeeded, error code otherwise.
+   */
+  AverStatus GetImageVersion(std::string* image_version_);
+
+  /**
+   * @brief Reads the device version.
+   * @param device_version_ Output device version string.
+   * @return NO_ERROR if read ok, error code otherwise.
+   */
+  AverStatus ReadDeviceVersion(std::string* device_version_);
+
+ private:
+
+  /**
+   * @brief Finds the device with usb_pid & usb_vid.
+   * @return device path to open the video device.
+   */
+  std::string FindDevice();
+
+  /**
+   * @brief Finds the hid device with usb_pid & usb_vid.
+   * @return device path to open the hid device.
+   */
+  std::string FindHidDevice();
+
+  /**
+   * @brief Control query data size needs to be known before querying.
+   * Therefore, UVC_GET_LEN needs to be queried for the size first.
+   * UVC_GET_LEN query returns 2 bytes of data in little endian. Refers to
+   * https://linuxtv.org/downloads/v4l-dvb-api/v4l-drviers/uvcvideo.html for
+   * more info.
+   * @param unitID XU unit id.
+   * @param control_selector XU control selector.
+   * @param data_size Data size output.
+   * @return NO_ERROR if succeeded, error code otherwise.
+   */
+  AverStatus QueryDataSize(unsigned char unit_id,
+                    unsigned char control_selector,
+                    int* data_size);
+
+
+  /**
+   * @brief Check device isp status.
+   * @return NO_ERROR if succeeded, error code otherwise.
+   */
+  AverStatus IspStatusGet();
+
+  /**
+   * @brief Send isp file name to CAM520.
+   * @return NO_ERROR if succeeded, error code otherwise.
+   */
+  AverStatus IspFileStart();
+
+  /**
+   * @brief Download isp file to CAM520.
+   * @param buffer Buffer full with firmware data.
+   * @param isp_file_size Firmware data size.
+   * @return NO_ERROR if succeeded, error code otherwise.
+   */
+  AverStatus IspFileDownload(std::vector<char> buffer, uint32_t isp_file_size);
+
+  /**
+   * @brief Send the commend that downdloading isp file is finish.
+   * @param isp_file_size Firmware data size.
+   * @return NO_ERROR if succeeded, error code otherwise.
+   */
+  AverStatus IspFileEnd(uint32_t isp_file_size);
+
+  /**
+   * @brief Send the commend to start isp follow.
+   * @return NO_ERROR if succeeded, error code otherwise.
+   */
+  AverStatus IspStart();
+
+  /**
+   * @brief Gets data from the extension control unit (XU).
+   * @param unit_id XU unit id.
+   * @param control_selector XU control selector.
+   * @param data XU data output.
+   * @return NO_ERROR if succeeded, error otherwise.
+   */
+  AverStatus GetXuControl(unsigned char unit_id,
+                   unsigned char control_selector,
+                   std::string* data);
+
+  /**
+   * @brief Sends data to the hid control unit.
+   * @param customize_cmd AVer hid command structure.
+   * @param data HID data output.
+   * @return NO_ERROR if succeeded, error otherwise.
+   */
+  AverStatus SendHidControl(const struct OutReport &customize_cmd);
+
+  std::string usb_pid_; // The string is USB product id.
+  int file_descriptor_; // File descriptor is for video device.
+  int file_descriptor_hid_; // Hid file descriptor is for hid device.
+  bool is_open_; // The device already opened.
+  std::string image_version_;  // Image version is firmware version from google cloud position.
+  std::string device_version_;  // Device version is firmware from the device.
+  std::vector<char> hid_return_msg_; // Messages return from hid device.
+
+  /**
+   * @brief Buffer puts isp firmware.
+   */
+  std::vector<char> firmware_buffer_;
+
+};
+#endif  // SRC_VIDEO_DEVICE_H_
\ No newline at end of file