From 35efa857ad1b46749e830033ca651dae880e329c Mon Sep 17 00:00:00 2001 From: Henry-Nicolas Tourneur Date: Tue, 22 Feb 2022 16:27:47 +0000 Subject: [PATCH] Import mfgtools_1.4.193.orig.tar.gz [dgit import orig mfgtools_1.4.193.orig.tar.gz] --- .gitignore | 15 + .gitmodules | 9 + .travis.yml | 40 + CMakeLists.txt | 35 + Doxyfile.in | 17 + LICENSE | 30 + README.md | 104 ++ appveyor.yml | 117 ++ libuuu/CMakeLists.txt | 64 + libuuu/backfile.h | 43 + libuuu/buffer.cpp | 1634 +++++++++++++++++ libuuu/buffer.h | 174 ++ libuuu/cmd.cpp | 1167 ++++++++++++ libuuu/cmd.h | 222 +++ libuuu/config.cpp | 208 +++ libuuu/config.h | 69 + libuuu/error.cpp | 77 + libuuu/fastboot.cpp | 965 ++++++++++ libuuu/fastboot.h | 270 +++ libuuu/fat.cpp | 207 +++ libuuu/fat.h | 103 ++ libuuu/ffu_format.h | 115 ++ libuuu/gen_ver.sh | 28 + libuuu/hidreport.cpp | 98 + libuuu/hidreport.h | 74 + libuuu/http.cpp | 470 +++++ libuuu/http.h | 63 + libuuu/libcomm.h | 159 ++ libuuu/liberror.h | 39 + libuuu/libuuu.h | 142 ++ libuuu/notify.cpp | 79 + libuuu/rominfo.cpp | 249 +++ libuuu/rominfo.h | 72 + libuuu/sdp.cpp | 773 ++++++++ libuuu/sdp.h | 218 +++ libuuu/sdps.cpp | 181 ++ libuuu/sdps.h | 54 + libuuu/sparse.cpp | 224 +++ libuuu/sparse.h | 60 + libuuu/sparse_format.h | 62 + libuuu/tar.cpp | 133 ++ libuuu/tar.h | 83 + libuuu/trans.cpp | 299 +++ libuuu/trans.h | 109 ++ libuuu/usbhotplug.cpp | 564 ++++++ libuuu/version.cpp | 70 + libuuu/zip.cpp | 330 ++++ libuuu/zip.h | 192 ++ msvc/bzip2.vcxproj | 227 +++ msvc/bzip2.vcxproj.filters | 52 + msvc/createversion.bat | 16 + msvc/libuuu.filters | 30 + msvc/libuuu.vcxproj | 218 +++ msvc/libuuu.vcxproj.filters | 123 ++ msvc/uuu-static-link.sln | 77 + msvc/uuu-static-link.vcxproj | 312 ++++ msvc/uuu.sln | 77 + msvc/uuu.vcxproj | 316 ++++ msvc/uuu.vcxproj.filters | 33 + msvc/zlib.vcxproj | 193 ++ msvc/zlib.vcxproj.filters | 72 + snap/gui/universal-update-utility.png | Bin 0 -> 6540 bytes snap/hooks/configure | 26 + snap/hooks/post-refresh | 7 + snap/local/LICENSE | 21 + .../bash-completion/universal-update-utility | 5 + .../launchers/universal-update-utility-launch | 64 + snap/local/screenshots/carbon-help.png | Bin 0 -> 78208 bytes snap/snapcraft.yaml | 151 ++ uuu/CMakeLists.txt | 77 + uuu/autocomplete.cpp | 230 +++ uuu/buildincmd.cpp | 349 ++++ uuu/buildincmd.h | 124 ++ uuu/emmc_burn_all.lst | 38 + uuu/emmc_burn_loader.lst | 35 + uuu/fat_write.lst | 12 + uuu/gen_txt_include.sh | 5 + uuu/nand_burn_loader.lst | 35 + uuu/qspi_burn_loader.lst | 43 + uuu/sd_burn_all.lst | 35 + uuu/sd_burn_loader.lst | 33 + uuu/spl_boot.lst | 26 + uuu/uuu.cpp | 1124 ++++++++++++ uuu/uuu.lst | 70 + 84 files changed, 14766 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .travis.yml create mode 100644 CMakeLists.txt create mode 100644 Doxyfile.in create mode 100644 LICENSE create mode 100644 README.md create mode 100644 appveyor.yml create mode 100644 libuuu/CMakeLists.txt create mode 100644 libuuu/backfile.h create mode 100644 libuuu/buffer.cpp create mode 100644 libuuu/buffer.h create mode 100644 libuuu/cmd.cpp create mode 100644 libuuu/cmd.h create mode 100644 libuuu/config.cpp create mode 100644 libuuu/config.h create mode 100644 libuuu/error.cpp create mode 100644 libuuu/fastboot.cpp create mode 100644 libuuu/fastboot.h create mode 100644 libuuu/fat.cpp create mode 100644 libuuu/fat.h create mode 100644 libuuu/ffu_format.h create mode 100755 libuuu/gen_ver.sh create mode 100644 libuuu/hidreport.cpp create mode 100644 libuuu/hidreport.h create mode 100644 libuuu/http.cpp create mode 100644 libuuu/http.h create mode 100644 libuuu/libcomm.h create mode 100644 libuuu/liberror.h create mode 100644 libuuu/libuuu.h create mode 100644 libuuu/notify.cpp create mode 100644 libuuu/rominfo.cpp create mode 100644 libuuu/rominfo.h create mode 100644 libuuu/sdp.cpp create mode 100644 libuuu/sdp.h create mode 100644 libuuu/sdps.cpp create mode 100644 libuuu/sdps.h create mode 100644 libuuu/sparse.cpp create mode 100644 libuuu/sparse.h create mode 100644 libuuu/sparse_format.h create mode 100644 libuuu/tar.cpp create mode 100644 libuuu/tar.h create mode 100644 libuuu/trans.cpp create mode 100644 libuuu/trans.h create mode 100644 libuuu/usbhotplug.cpp create mode 100644 libuuu/version.cpp create mode 100644 libuuu/zip.cpp create mode 100644 libuuu/zip.h create mode 100644 msvc/bzip2.vcxproj create mode 100644 msvc/bzip2.vcxproj.filters create mode 100644 msvc/createversion.bat create mode 100644 msvc/libuuu.filters create mode 100644 msvc/libuuu.vcxproj create mode 100644 msvc/libuuu.vcxproj.filters create mode 100644 msvc/uuu-static-link.sln create mode 100644 msvc/uuu-static-link.vcxproj create mode 100644 msvc/uuu.sln create mode 100644 msvc/uuu.vcxproj create mode 100644 msvc/uuu.vcxproj.filters create mode 100644 msvc/zlib.vcxproj create mode 100644 msvc/zlib.vcxproj.filters create mode 100644 snap/gui/universal-update-utility.png create mode 100755 snap/hooks/configure create mode 100644 snap/hooks/post-refresh create mode 100644 snap/local/LICENSE create mode 100644 snap/local/bash-completion/universal-update-utility create mode 100755 snap/local/launchers/universal-update-utility-launch create mode 100644 snap/local/screenshots/carbon-help.png create mode 100644 snap/snapcraft.yaml create mode 100644 uuu/CMakeLists.txt create mode 100644 uuu/autocomplete.cpp create mode 100644 uuu/buildincmd.cpp create mode 100644 uuu/buildincmd.h create mode 100644 uuu/emmc_burn_all.lst create mode 100644 uuu/emmc_burn_loader.lst create mode 100644 uuu/fat_write.lst create mode 100755 uuu/gen_txt_include.sh create mode 100644 uuu/nand_burn_loader.lst create mode 100644 uuu/qspi_burn_loader.lst create mode 100644 uuu/sd_burn_all.lst create mode 100644 uuu/sd_burn_loader.lst create mode 100644 uuu/spl_boot.lst create mode 100644 uuu/uuu.cpp create mode 100644 uuu/uuu.lst diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..677010c --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +Debug +Release +gitversion.h +*.user +.vs +CMakeFiles +*.cmake +*.swp +*.a +*.so +uuu/uuu +Makefile +CMakeCache.txt +*.clst +*.snap diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b7eb42d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "libusb"] + path = libusb + url = https://github.com/nxpfrankli/libusb.git +[submodule "zlib"] + path = zlib + url = https://github.com/madler/zlib.git +[submodule "bzip2"] + path = bzip2 + url = git://sourceware.org/git/bzip2.git diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..1d2d235 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,40 @@ +language: c++ + +matrix: + include: + + - os: osx + osx_image: xcode9.4 + compiler: clang + + - os: osx + osx_image: xcode9.4 + compiler: gcc + + - os: osx + osx_image: xcode10.1 + compiler: clang + + - os: osx + osx_image: xcode10.1 + compiler: gcc + + - os: osx + osx_image: xcode11.6 + compiler: clang + + - os: osx + osx_image: xcode11.6 + compiler: gcc + +addons: + homebrew: + update: true + packages: + - cmake + - libusb + - openssl + - pkg-config + +script: + - cmake -DOPENSSL_ROOT_DIR=$(brew --prefix)/opt/openssl . && make diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..90a0197 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.4) + +project(uuu) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_SKIP_RPATH ON) + +option(BUILD_DOC "Build documentation" OFF) + +add_subdirectory(libuuu) +add_subdirectory(uuu) + +if (BUILD_DOC) + # check if Doxygen is installed + find_package(Doxygen) + if (DOXYGEN_FOUND) + # set input and output files + set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + # request to configure the file + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + message("Doxygen build started") + + # note the option ALL which allows to build the docs together with the application + add_custom_target( doc_doxygen ALL + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating documentation with Doxygen" + VERBATIM ) + else (DOXYGEN_FOUND) + message("Doxygen need to be installed to generate the doxygen documentation") + endif (DOXYGEN_FOUND) +endif (BUILD_DOC) diff --git a/Doxyfile.in b/Doxyfile.in new file mode 100644 index 0000000..f925937 --- /dev/null +++ b/Doxyfile.in @@ -0,0 +1,17 @@ +PROJECT_NAME = "uuu" +PROJECT_BRIEF = "uuu (Universal Update Utility), mfgtools 3.0" +DOXYFILE_ENCODING = UTF-8 + +OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/docs/ +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/uuu/ @CMAKE_CURRENT_SOURCE_DIR@/libuuu/ +RECURSIVE = YES + +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_PACKAGE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = YES + +CALL_GRAPH = YES +CALLER_GRAPH = YES diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2092934 --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +Copyright 2018 NXP. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the name of the Freescale Semiconductor nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..6910276 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# uuu (Universal Update Utility), mfgtools 3.0 + +[![Build status](https://ci.appveyor.com/api/projects/status/github/NXPmicro/mfgtools?svg=true)](https://ci.appveyor.com/project/nxpfrankli/mfgtools-kvqcg) +[![Build Status](https://travis-ci.com/NXPmicro/mfgtools.svg?branch=master)](https://travis-ci.com/NXPmicro/mfgtools) + +![GitHub](https://img.shields.io/github/license/NXPmicro/mfgtools.svg) + +[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/universal-update-utility) sudo snap install universal-update-utility; + +Freescale/NXP I.MX Chip image deploy tools. +**original linux version uses "linux" branch, windows version uses "windows" branch** + + uuu (universal update utility) for nxp imx chips -- libuuu-1.0.1-gffd9837 + + Succeded:0 Failed:3 Wait for Known USB Devices to Appear... + + 1:11 5/5 [ ] SDP: jump -f u-boot-dtb.imx -ivtinitramf.... + 2:1 1/5 [===> ] SDP: boot -f u-boot-imx7dsabresd_sd.imx .... + +# Key features + - The real cross platform. Linux, Windows, MacOS(not test yet) + - Multi devices program support + - Daemon mode support + - Few depedencies (only libusb, zlibc, libbz2) + - Firmware (uboot/kernel) uses WCID to auto load the winusb driver on the Windows side. Windows7 users need to install the winusb driver from https://zadig.akeo.ie/ Windows10 will install the driver automatically. + +# Examples: +``` + uuu u-boot.imx Download u-boot.imx via HID device + + uuu list.uu Run all the commands in list.uu + + uuu -s Enter shell mode. Input command. + + uuu -v u-boot.imx verbose mode + + uuu -d u-boot.imx Once it detects the attachement of a known device, download boot.imx. + + u-boot.imx can be replaced, new file will be download once board reset. + + Do not unplug the SD card, write to the SD card, nor plug in a SD card when debugging uboot. + + uuu -b emmc u-boot.imx write u-boot.imx to emmc boot partition. u-boot.imx need enable fastboot + + uuu -b emmc_all u-boot.imx sdcard.bz2\* + decompress sdcard.bz2 file and download the whole image into emmc +``` + +# Prebuilt Image and pdf document + +The prebuilt image and document are here: + - https://github.com/NXPmicro/mfgtools/releases + - UUU.pdf is snapshot of [wiki](https://github.com/NXPmicro/mfgtools/wiki) + +# How to Build: + +## Windows +- `git clone https://github.com/NXPmicro/mfgtools.git` +- `cd mfgtools` +- `git submodule init` +- `git submodule update` +- `open msvs/uuu.sln with Visual Studio 2017` + +Visual Studio + +Note that, since uuu is an OSI compliant Open Source project, you are entitled to download and use the freely available Visual Studio Community Edition to build, run or develop for uuu. As per the Visual Studio Community Edition license this applies regardless of whether you are an individual or a corporate user. + +## Linux +- `git clone https://github.com/NXPmicro/mfgtools.git` +- `cd mfgtools` +- `sudo apt-get install libusb-1.0-0-dev libbz2-dev pkg-config cmake libssl-dev g++` +- `cmake . && make` + +The above commands build mfgtools in source. To build it out of source +(requires cmake 3.13 or newer): +- `cmake -S . -B build` +- `cmake --build build --target all` + +For cmake prior 3.13: +- `mkdir build && cd build` +- `cmake .. && make` + +## macOS +- `git clone https://github.com/NXPmicro/mfgtools.git` +- `cd mfgtools` +- `brew install cmake libusb openssl pkg-config` +- `cmake -DOPENSSL_ROOT_DIR=$(brew --prefix)/opt/openssl . && make` + +Note that we assume [brew](https://brew.sh) is installed and can be used to resolve dependencies as shown above. The remaining dependency `libbz2` can be resolved via the XCode supplied libraries. + +# Run environment + - Windows 10 64 bit + - Linux (Ubuntu) 64 bit + - macOS (Catalina) + - 32 bit systems will have problems with big files. + +# License +uuu is licensed under the BSD license. See LICENSE. +The BSD licensed prebuilt Windows binary version of uuu is statically linked with the LGPL libusb library, which remains LGPL. + + - bzip2 (BSD license) is from https://github.com/enthought/bzip2-1.0.6 + - zlib (zlib license) is from https://github.com/madler/zlib.git + - libusb (LGPL-2.1) is from https://github.com/libusb/libusb.git + diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..e679f84 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,117 @@ +version: 1.4.{build} +image: + - Visual Studio 2019 + - Visual Studio 2017 + - Ubuntu1604 + - macOS + +configuration: + - Debug + - Release + +platform: + - x86 + - x64 + +init: + - sh: if [ "${CONFIGURATION}" = "Debug" ] ; then exit 0; fi + - sh: if [ "${PLATFORM}" = "x86" ]; then exit 0; fi + +skip_tags: true + +install: +- cmd: echo %APPVEYOR_BUILD_FOLDER% + +- cmd: git submodule update --init + +- cmd: cd %APPVEYOR_BUILD_FOLDER%\libusb + +- cmd: cd .. + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = "macOS" ]; then brew install libusb pkg-config; fi + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then sudo apt-get update; fi + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then sudo DEBIAN_FRONTEND=noninteractive apt-get --yes --force-yes install libusb-1.0-0-dev libbz2-dev asciidoc rename; fi + +build_script: + +# below powershell actions equals to retarget projects to newest SDK and tool v142 in visual studio 2019 +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc msvc\uuu.vcxproj) -replace '141', '142' | Out-File -encoding ASCII msvc\uuu.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc msvc\uuu.vcxproj) -replace '10.0.16299.0', '10.0' | Out-File -encoding ASCII msvc\uuu.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc msvc\bzip2.vcxproj) -replace '141', '142' | Out-File -encoding ASCII msvc\bzip2.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc msvc\bzip2.vcxproj) -replace '10.0.16299.0', '10.0' | Out-File -encoding ASCII msvc\bzip2.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc msvc\libuuu.vcxproj) -replace '141', '142' | Out-File -encoding ASCII msvc\libuuu.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc msvc\libuuu.vcxproj) -replace '10.0.16299.0', '10.0' | Out-File -encoding ASCII msvc\libuuu.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc msvc\zlib.vcxproj) -replace '141', '142' | Out-File -encoding ASCII msvc\zlib.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc msvc\zlib.vcxproj) -replace '10.0.16299.0', '10.0' | Out-File -encoding ASCII msvc\zlib.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc libusb\msvc\libusb_dll_2017.vcxproj) -replace '10.0.16299.0', '10.0' | Out-File -encoding ASCII libusb\msvc\libusb_dll_2017.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc libusb\msvc\libusb_dll_2017.vcxproj) -replace '141', '142' | Out-File -encoding ASCII libusb\msvc\libusb_dll_2017.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc msvc\uuu-static-link.vcxproj) -replace '141', '142' | Out-File -encoding ASCII msvc\uuu-static-link.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc msvc\uuu-static-link.vcxproj) -replace '10.0.16299.0', '10.0' | Out-File -encoding ASCII msvc\uuu-static-link.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc libusb\msvc\libusb_static_2017.vcxproj) -replace '10.0.16299.0', '10.0' | Out-File -encoding ASCII libusb\msvc\libusb_static_2017.vcxproj} +- ps: If($env:APPVEYOR_BUILD_WORKER_IMAGE -eq "Visual Studio 2019") {(gc libusb\msvc\libusb_static_2017.vcxproj) -replace '141', '142' | Out-File -encoding ASCII libusb\msvc\libusb_static_2017.vcxproj} + + +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2019" (msbuild %APPVEYOR_BUILD_FOLDER%/msvc/uuu.sln /verbosity:minimal /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll") +#coverity tool is only available to visual studio 2017 imagine in appveyor +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" (cov-build --dir cov-int msbuild %APPVEYOR_BUILD_FOLDER%/msvc/uuu.sln /verbosity:minimal /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll") + +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" (7z a -tzip mfg.zip cov-int) + +- cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2017" (curl --form token=%coverity_token% --form email=frank.li@nxp.com --form file=@mfg.zip --form version="automation test" --form description="testing coverity automation" https://scan.coverity.com/builds?project=NXPmicro%2Fmfgtools) + +- cmd: git clean -dxf . + +- cmd: msbuild %APPVEYOR_BUILD_FOLDER%/msvc/uuu-static-link.sln /verbosity:minimal /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + +- cmd: if exist %APPVEYOR_BUILD_FOLDER%\msvc\x64\release\uuu.exe cp %APPVEYOR_BUILD_FOLDER%\msvc\x64\release\uuu.exe %APPVEYOR_BUILD_FOLDER%\uuu.exe + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = "macOS" ]; then export PATH="/usr/local/Cellar/pkg-config/0.29.2_3/bin:${PATH}"; pkg-config --list-all; cmake -DOPENSSL_ROOT_DIR=$(brew --prefix)/opt/openssl; else cmake -D 'STATIC=1' .; fi +- sh: make +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = "macOS" ]; then ls uuu; mv uuu/uuu uuu/uuu_mac; fi + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then git submodule init; fi +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then git submodule update; fi +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then git archive --prefix "uuu-${APPVEYOR_BUILD_VERSION}/" -o "uuu_source-${APPVEYOR_BUILD_VERSION}.tar" HEAD ; fi +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then git submodule foreach --recursive "git archive --prefix=uuu-${APPVEYOR_BUILD_VERSION}/\$path/ --output=\$sha1.tar HEAD && tar --concatenate --file=$(pwd)/uuu_source-${APPVEYOR_BUILD_VERSION}.tar \$sha1.tar && rm \$sha1.tar" ; fi +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then mkdir uuu-${APPVEYOR_BUILD_VERSION}; git describe --tags --long >uuu-${APPVEYOR_BUILD_VERSION}/.tarball-version ; fi +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then tar -r uuu-${APPVEYOR_BUILD_VERSION}/.tarball-version -f uuu_source-${APPVEYOR_BUILD_VERSION}.tar ; fi +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then gzip uuu_source-${APPVEYOR_BUILD_VERSION}.tar; fi +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then tar xzf uuu_source-${APPVEYOR_BUILD_VERSION}.tar.gz && zip uuu_source-${APPVEYOR_BUILD_VERSION}.zip $(tar tf uuu_source-${APPVEYOR_BUILD_VERSION}.tar.gz); fi + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then git clone https://github.com/NXPmicro/mfgtools.wiki.git; fi + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then cd mfgtools.wiki; fi + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then rename -f 's/\.asciidoc$//' *; fi + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then echo "" > UUU-docinfo.xml; fi + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then git log -n25 --reverse --format="format:%h%cd%an%s" >> UUU-docinfo.xml; fi + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then echo "" >> UUU-docinfo.xml; fi + +- sh: if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" != "macOS" ]; then a2x -L -a docinfo UUU; fi + +artifacts: +- path: uuu.exe +- path: uuu/uuu +- path: uuu/uuu_mac +- path: mfgtools.wiki/UUU.pdf +- path: uuu_source-${APPVEYOR_BUILD_VERSION}.tar.gz +- path: uuu_source-${APPVEYOR_BUILD_VERSION}.zip + +before_deploy: +- cmd: '' +deploy: +- provider: GitHub + description: prebuild for $(APPVEYOR_REPO_COMMIT) \n\n $(APPVEYOR_REPO_COMMIT_MESSAGE) + auth_token: + secure: SWWVkwSfPyVIaPChBBl+uAA3Fau9Rl5iNPQ9VRL8yyggXvc6wPcr/O9iXBMVM7Ju + artifact: uuu.exe; uuu/uuu; uuu/uuu_mac; mfgtools.wiki/UUU.pdf; uuu_source-${APPVEYOR_BUILD_VERSION}.tar.gz; uuu_source-${APPVEYOR_BUILD_VERSION}.zip + draft: true + +environment: + coverity_token: + secure: 5VvyV4fYfI6xPsqaeDHvBamkUmmVNjZj0J5pLLQ6NCw= diff --git a/libuuu/CMakeLists.txt b/libuuu/CMakeLists.txt new file mode 100644 index 0000000..2ace072 --- /dev/null +++ b/libuuu/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required(VERSION 3.4) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_SKIP_RPATH ON) + +find_package(BZip2 REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(LIBUSB REQUIRED libusb-1.0>=1.0.16) +find_package(Threads) + +if (STATIC) +set(OPENSSL_USE_STATIC_LIBS TRUE) +endif() + +find_package(OpenSSL) + +if(OPENSSL_FOUND) +set(UUUSSL "-DUUUSSL") +set(UUUOPENSLL_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR}) +endif() + +include_directories(${LIBUSB_INCLUDE_DIRS} ${UUUOPENSLL_INCLUDE_DIR} include) + + +set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wstrict-aliasing -Wextra ${UUUSSL}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 ${UUUSSL}") + +set(SOURCES + error.cpp + buffer.cpp + cmd.cpp + config.cpp + notify.cpp + sdps.cpp + trans.cpp + usbhotplug.cpp + version.cpp + sdp.cpp + gitversion.h + fastboot.cpp + zip.cpp + fat.cpp + tar.cpp + rominfo.cpp + http.cpp + hidreport.cpp + sparse.cpp +) + +set(generated_files_dir "${CMAKE_BINARY_DIR}/libuuu/gen") +set(gitversion_h "${generated_files_dir}/gitversion.h") + +add_custom_command( + OUTPUT gitversion.h + PRE_BUILD + COMMAND mkdir -p ${generated_files_dir} + COMMAND sh -c 'cd ${CMAKE_CURRENT_SOURCE_DIR} && rm -f ${gitversion_h} && ./gen_ver.sh "${gitversion_h}.tmp" && mv -f "${gitversion_h}.tmp" "${gitversion_h}"' + +) +include_directories(${generated_files_dir}) + +#add_library( uuc SHARED ${SOURCES} )) +add_library( uuc_s STATIC ${SOURCES} ) diff --git a/libuuu/backfile.h b/libuuu/backfile.h new file mode 100644 index 0000000..d40e2ca --- /dev/null +++ b/libuuu/backfile.h @@ -0,0 +1,43 @@ +/* + * Copyright 2020 NXP. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of the NXP Semiconductor nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include + +class Backfile +{ +public: + const std::string& get_filename() const noexcept { return m_filename; } + +protected: + std::string m_filename; +}; diff --git a/libuuu/buffer.cpp b/libuuu/buffer.cpp new file mode 100644 index 0000000..f723552 --- /dev/null +++ b/libuuu/buffer.cpp @@ -0,0 +1,1634 @@ +/* +* Copyright 2018-2019 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include +#include "buffer.h" +#include +#include "liberror.h" +#include +#include +#include "libcomm.h" +#include "libuuu.h" +#include "zip.h" +#include "fat.h" +#include "tar.h" +#include +#include "bzlib.h" +#include "stdio.h" +#include +#include "http.h" + +#ifdef WIN32 +#define stat_os _stat64 +#elif defined(__APPLE__) +#define stat_os stat +#include "dirent.h" +#else +#define stat_os stat64 +#include "dirent.h" +#endif + +static map> g_filebuffer_map; +static mutex g_mutex_map; + +#define MAGIC_PATH '>' + +string g_current_dir = ">"; + +void set_current_dir(const string &dir) +{ + g_current_dir = MAGIC_PATH; + g_current_dir += dir; +} + +class FSBasic +{ +public: + virtual int get_file_timesample(const string &filename, uint64_t *ptime) = 0; + virtual int load(const string &backfile, const string &filename, shared_ptr p, bool async) = 0; + virtual bool exist(const string &backfile, const string &filename) = 0; + virtual int for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) = 0; + virtual int split(const string &filename, string *outbackfile, string *outfilename, bool dir=false) + { + string path = str_to_upper(filename); + if (m_ext == nullptr || strlen(m_ext) == 0) + { + if(dir) + { + size_t pos = path.rfind("/"); + if(pos == string::npos) + { + *outbackfile = MAGIC_PATH; + *outbackfile += "./"; + *outfilename = filename; + } else { + *outbackfile = filename.substr(0, pos); + if(filename.size() >= pos + 1) + *outfilename = filename.substr(pos + 1); + else + outfilename->clear(); + } + }else + { + *outbackfile = filename; + } + return 0; + } + + string ext = m_ext; + if(!dir) + ext += "/"; + size_t pos = path.rfind(ext); + if (pos == string::npos) + { + set_last_err_string("can't find ext name in path"); + return -1; + } + + *outbackfile = filename.substr(0, pos + strlen(m_ext)); + + if(filename.size() >= pos + strlen(m_ext) + 1) + *outfilename = filename.substr(pos + strlen(m_ext) + 1); + else + outfilename->clear(); + return 0; + } + +protected: + const char * m_ext = nullptr; + const char * m_Prefix = nullptr; +}; + +static class FSFlat: public FSBasic +{ +public: + FSFlat() { m_ext = ""; } + int get_file_timesample(const string &filename, uint64_t *ptime) override + { + struct stat_os st; + if (stat_os(filename.c_str() + 1, &st)) + { + set_last_err_string("stat_os failure"); + return -1; + } + + *ptime = st.st_mtime; + + return 0; + } + + bool exist(const string &backfile, const string &filename) override + { + struct stat_os st; + return stat_os(backfile.c_str() + 1, &st) == 0 && ((st.st_mode & S_IFDIR) == 0); + } + + int load(const string &backfile, const string &filename, shared_ptr p, bool async) override + { + struct stat_os st; + if (stat_os(backfile.c_str() + 1, &st)) + { + set_last_err_string("stat_os failure"); + return -1; + } + p->unmapfile(); + + if (p->mapfile(backfile.substr(1), st.st_size)) + return -1; + + p->m_avaible_size = st.st_size; + + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + + return 0; + } + + int for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) override + { + struct stat_os st; + + if(stat_os(backfile.c_str() + 1, &st)) + { + return -1; + } + + if(st.st_mode & S_IFDIR) + { +#ifdef WIN32 + string str = backfile.substr(1); + if (filename.empty()) + str += "/*"; + else + str += "/" + filename; + + WIN32_FIND_DATA fd; + HANDLE handle = FindFirstFile(str.c_str(), &fd); + BOOL b = false; + do + { + if (handle == INVALID_HANDLE_VALUE) + break; + string path = backfile + "/" + fd.cFileName; + if(fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) + path += "/"; + fn(path.c_str() + 1, p); + } while (FindNextFile(handle, &fd)); + CloseHandle(handle); + return 0; +#else + DIR *dir; + dir = opendir(backfile.c_str() + 1); + struct dirent *dp; + while ((dp=readdir(dir)) != nullptr) + { + string name = dp->d_name; + if(name.substr(0, filename.size()) == filename || filename.empty()) + { + string path = backfile + "/" + name; + if(dp->d_type == DT_DIR) + path += "/"; + fn(path.c_str() + 1, p); + } + } + closedir(dir); + return 0; +#endif + }else + { + return fn(backfile.c_str() + 1, p); + } + } + +} g_fsflat; + +class FSNetwork : public FSBasic +{ +protected: + int m_Port; + +public: + int split(const string &filename, string *outbackfile, string *outfilename, bool dir = false) override + { + if (m_Prefix == nullptr) + return -1; + + if (filename.size() < strlen(m_Prefix)) + return -1; + + string path = str_to_upper(filename); + if (path.compare(1, strlen(m_Prefix), m_Prefix) == 0) + { + size_t pos; + pos = filename.find('/', 1 + strlen(m_Prefix)); + + *outbackfile = filename.substr(1 + strlen(m_Prefix), pos - 1 - strlen(m_Prefix)); + + size_t cpos; + cpos = outbackfile->find(':'); + if (cpos != string::npos) + { + m_Port = str_to_uint32(outbackfile->substr(cpos + 1)); + + *outbackfile = outbackfile->substr(0, cpos); + } + *outfilename = filename.substr(pos); + + return 0; + } + + return -1; + } +}; + +static class FSHttp : public FSNetwork +{ +public: + FSHttp() { m_Prefix = "HTTP://"; m_Port = 80; } + int load(const string &backfile, const string &filename, shared_ptr p, bool async) override; + virtual bool exist(const string &backfile, const string &filename) override { return true; }; + int for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) override { return 0; }; + int get_file_timesample(const string &filename, uint64_t *ptime) override { return 0; }; +}g_fshttp; + +static class FSHttps : public FSHttp +{ +public: + FSHttps() { m_Prefix = "HTTPS://"; m_Port = 443; } +}g_fshttps; + +int http_load(shared_ptr http, shared_ptr p, string filename) +{ + size_t max = 0x10000; + + uuu_notify ut; + ut.type = uuu_notify::NOTIFY_DOWNLOAD_START; + ut.str = (char*)filename.c_str(); + call_notify(ut); + + ut.type = uuu_notify::NOTIFY_TRANS_SIZE; + ut.total = p->size(); + call_notify(ut); + + for (size_t i = 0; i < p->size(); i += max) + { + size_t sz = p->size() - i; + if (sz > max) + sz = max; + if (http->HttpDownload((char*)(p->data() + i), sz) < 0) + { + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_ERROR_BIT); + p->m_request_cv.notify_all(); + return -1; + } + p->m_avaible_size = i + sz; + p->m_request_cv.notify_all(); + + ut.type = uuu_notify::NOTIFY_TRANS_POS; + ut.total = i + sz; + call_notify(ut); + } + + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + + ut.type = uuu_notify::NOTIFY_DOWNLOAD_END; + ut.str = (char*)filename.c_str(); + call_notify(ut); + return 0; +} + +int FSHttp::load(const string &backfile, const string &filename, shared_ptr p, bool async) +{ + shared_ptr http = make_shared(); + + if (http->HttpGetHeader(backfile, filename, m_Port, typeid(*this) == typeid(FSHttps))) + return -1; + + size_t sz = http->HttpGetFileSize(); + + p->resize(sz); + + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_KNOWN_SIZE); + p->m_request_cv.notify_all(); + + if (async) + { + p->m_aync_thread = thread(http_load, http, p, backfile + filename); +#ifdef WIN32 + SetThreadPriority(p->m_aync_thread.native_handle(), THREAD_PRIORITY_BELOW_NORMAL); +#endif + } + else + { + if (http_load(http, p, backfile + filename)) + return -1; + + p->m_avaible_size = p->m_DataSize; + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + } + + return 0; +} + +class FSBackFile : public FSBasic +{ +public: + int get_file_timesample(const string &filename, uint64_t *ptime) override; + +}; + +static class FSZip : public FSBackFile +{ +public: + FSZip() { m_ext = ".ZIP"; }; + int load(const string &backfile, const string &filename, shared_ptr p, bool async) override; + bool exist(const string &backfile, const string &filename) override; + int for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) override; +}g_fszip; + +static class FSTar: public FSBackFile +{ +public: + FSTar() {m_ext = ".TAR"; }; + int load(const string &backfile, const string &filename, shared_ptr p, bool async) override; + bool exist(const string &backfile, const string &filename) override; + int for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) override; +}g_fstar; + + +static class FSFat : public FSBackFile +{ +public: + FSFat() { m_ext = ".SDCARD"; }; + int load(const string &backfile, const string &filename, shared_ptr p, bool async) override; + bool exist(const string &backfile, const string &filename) override; + int for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) override; +}g_fsfat; + +class FSCompressStream : public FSBackFile +{ +public: + bool exist(const string &backfile, const string &filename) override; + int for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) override; +}; + +static class FSBz2 : public FSCompressStream +{ +public: + FSBz2() { m_ext = ".BZ2"; }; + int load(const string &backfile, const string &filename, shared_ptr p, bool async) override; +}g_fsbz2; + +static class FSGz : public FSCompressStream +{ +public: + FSGz() { m_ext = ".GZ"; }; + int load(const string &backfile, const string &filename, shared_ptr p, bool async) override; +}g_fsgz; + +static class FS_DATA +{ +public: + vector m_pFs; + FS_DATA() + { + m_pFs.push_back(&g_fsflat); + m_pFs.push_back(&g_fszip); + m_pFs.push_back(&g_fstar); + m_pFs.push_back(&g_fsbz2); + m_pFs.push_back(&g_fsfat); + m_pFs.push_back(&g_fsgz); + m_pFs.push_back(&g_fshttps); + m_pFs.push_back(&g_fshttp); + } + + int get_file_timesample(const string &filename, uint64_t *ptimesame) + { + if (ptimesame == nullptr) + { + set_last_err_string("ptimesame is null\n"); + return -1; + } + + for (int i = 0; i < m_pFs.size(); i++) + { + if (!m_pFs[i]->get_file_timesample(filename, ptimesame)) + return 0; + } + + return -1; + } + + int for_each_ls(uuu_ls_file fn, string path, void *p) + { + for (int i = m_pFs.size() -1; i >= 0; i--) + { + string back, filename; + if (m_pFs[i]->split(path, &back, &filename, true) == 0) + if(m_pFs[i]->for_each_ls(fn, back, filename, p)==0) + { + return 0; + } + } + return 0; + } + + bool exist(const string &filename) + { + for (int i = 0; i < m_pFs.size(); i++) + { + string back, fn; + if (m_pFs[i]->split(filename, &back, &fn) == 0) + if (m_pFs[i]->exist(back, fn)) + return true; + } + return false; + } + int load(const string &filename, shared_ptr p, bool async) + { + for (int i = 0; i < m_pFs.size(); i++) + { + string back, fn; + if (m_pFs[i]->split(filename, &back, &fn) == 0) + if(m_pFs[i]->load(back, fn, p, async) == 0) + return 0; + } + + string err; + err = "fail open file: "; + err += filename; + set_last_err_string(err); + return -1; + } +}g_fs_data; + +int FSBackFile::get_file_timesample(const string &filename, uint64_t *ptime) +{ + string back, file; + if (split(filename, &back, &file)) + return -1; + + return g_fs_data.get_file_timesample(back, ptime); +} + +bool FSZip::exist(const string &backfile, const string &filename) +{ + Zip zip; + if (zip.Open(backfile)) + return false; + + return zip.check_file_exist(filename); +} + +int FSZip::for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) +{ + Zip zip; + + if (zip.Open(backfile)) + return -1; + + for(auto it = zip.m_filemap.begin(); it!=zip.m_filemap.end(); ++it) + { + if(it->first.substr(0, filename.size()) == filename || filename.empty()) + { + string name = backfile; + name += "/"; + name += it->first; + fn(name.c_str()+1, p); + } + } + + return 0; +} + +int zip_async_load(string zipfile, string fn, shared_ptr buff) +{ + std::lock_guard lock(buff->m_async_mutex); + + Zip zip; + if (zip.Open(zipfile)) + return -1; + + if(zip.get_file_buff(fn, buff)) + return -1; + + buff->m_avaible_size = buff->m_DataSize; + atomic_fetch_or(&buff->m_dataflags, FILEBUFFER_FLAG_LOADED); + + buff->m_request_cv.notify_all(); + return 0; +} + +int FSZip::load(const string &backfile, const string &filename, shared_ptr p, bool async) +{ + Zip zip; + + if (zip.Open(backfile)) + return -1; + + if (!zip.check_file_exist(filename)) + return -1; + + if (async) + { + p->m_aync_thread = thread(zip_async_load, backfile, filename, p); + } + else + { + if(zip.get_file_buff(filename, p)) + return -1; + + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + } + return 0; +} + +bool FSTar::exist(const string &backfile, const string &filename) +{ + Tar tar; + if (tar.Open(backfile)) + return false; + + return tar.check_file_exist(filename); +} + + +int FSTar::for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) +{ + Tar tar; + + if (tar.Open(backfile)) + return -1; + + for(auto it = tar.m_filemap.begin(); it!=tar.m_filemap.end(); ++it) + { + if(it->first.substr(0, filename.size()) == filename || filename.empty()) + { + string name = backfile; + name += "/"; + name += it->first; + fn(name.c_str()+1, p); + } + } + + return 0; +} + +int FSTar::load(const string &backfile, const string &filename, shared_ptr p, bool async) +{ + Tar tar; + if (tar.Open(backfile)) + return -1; + + if (!tar.check_file_exist(filename)) + return -1; + + if(tar.get_file_buff(filename, p)) + return -1; + p->m_avaible_size = p->m_DataSize; + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + return 0; +} + +bool FSFat::exist(const string &backfile, const string &filename) +{ + Fat fat; + if (fat.Open(backfile)) + { + return false; + } + return fat.m_filemap.find(filename) != fat.m_filemap.end(); +} + +int FSFat::load(const string &backfile, const string &filename, shared_ptr p, bool async) +{ + Fat fat; + if (fat.Open(backfile)) + { + return -1; + } + + if(fat.get_file_buff(filename, p)) + return -1; + + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + + return 0; +} + +int FSFat::for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) +{ + Fat fat; + if (fat.Open(backfile)) + { + return -1; + } + + for(auto it = fat.m_filemap.begin(); it != fat.m_filemap.end(); ++it) + { + if(it->first.substr(0, filename.size()) == filename || filename.empty()) + { + string name = backfile; + name += "/"; + name += it->first; + fn(name.c_str()+1, p); + } + } + return 0; +} + +bool FSCompressStream::exist(const string &backfile, const string &filename) +{ + if (filename == "*") + return true; + + return false; +} + +int FSCompressStream::for_each_ls(uuu_ls_file fn, const string &backfile, const string &filename, void *p) +{ + + if(!g_fs_data.exist(backfile)) + return -1; + + string str; + str = backfile + "/*"; + + fn(str.c_str() + 1, p); + return 0; +} + +struct bz2_blk +{ + size_t start; + size_t size; + size_t decompress_offset; + size_t decompress_size; + size_t actual_size; + int error; +}; + +class bz2_blks +{ +public: + vector blk; + mutex blk_mutex; + + condition_variable cv; + mutex con_mutex; + + atomic top; + atomic bottom; + bz2_blks() { top = 0; bottom = ULLONG_MAX; } +}; + +int bz2_update_available(shared_ptr p, bz2_blks * pblk) +{ + lock_guard lock(pblk->blk_mutex); + size_t sz = 0; + for (int i = 1; i < pblk->blk.size() - 1; i++) + { + if (pblk->blk[i].error) + break; + + if (!pblk->blk[i].actual_size) + break; + + sz += pblk->blk[i].actual_size; + } + + p->m_avaible_size = sz; + p->m_request_cv.notify_all(); + return 0; +} + +int bz2_decompress(shared_ptr pbz, shared_ptr p, bz2_blks * pblk) +{ + bz2_blk one; + size_t cur; + vector buff; + + while (pblk->top + 1 < pblk->bottom) + { + if (p->IsError()) + return -1; + + { + std::unique_lock lck(pblk->con_mutex); + while (pblk->top + 1 >= pblk->blk.size()) { + pblk->cv.wait(lck); + if (p->IsError()) + return -1; + } + } + + { + lock_guard lock(pblk->blk_mutex); + if (pblk->top < pblk->blk.size() - 1) + { + cur = pblk->top; + one = pblk->blk[pblk->top]; + pblk->top++; + } + else + { + continue; + } + } + + unsigned int len = one.decompress_size; + buff.resize(len); + + one.error = BZ2_bzBuffToBuffDecompress((char*)buff.data(), + &len, + (char*)pbz->data() + one.start, + one.size, + 0, + 0); + one.actual_size = len; + + { + lock_guard lock(pblk->blk_mutex); + (*pblk).blk[cur] = one; + } + + + { + lock_guard lock(p->m_data_mutex); + if (p->size() < one.decompress_offset + one.actual_size) + if(p->resize(one.decompress_offset + one.actual_size)) + return -1; + + memcpy(p->data() + one.decompress_offset, buff.data(), one.actual_size); + } + + bz2_update_available(p, pblk); + + } + + return 0; +} + +int bz_async_load(string filename, shared_ptr p) +{ + shared_ptr pbz; + + pbz = get_file_buffer(filename, true); + if (pbz == nullptr) { + string err; + err = "Failure get file buffer: "; + err += filename; + set_last_err_string(err); + return -1; + } + + bz2_blks blks; + bz2_blk one; + memset(&one, 0, sizeof(one)); + blks.blk.push_back(one); + blks.top = 1; + + size_t total = 0; + + uint8_t *p1 = &pbz->at(0); + + int nthread = thread::hardware_concurrency(); + + vector threads; + + if (p->reserve(pbz->size() * 5)) //estimate uncompressed memory size; + { + set_last_err_string("Out of memory"); + return -1; + } + + for (int i = 0; i < nthread; i++) + { + threads.push_back(thread(bz2_decompress, pbz, p, &blks)); +#ifdef WIN32 + if( i!=0 ) + SetThreadPriority(threads[i].native_handle(), THREAD_PRIORITY_BELOW_NORMAL); +#endif + } + + size_t requested = 0; + for (size_t i = 0; i < pbz->size() - 10; i++) + { + if(i >= requested) + { + requested = i + 0x10000; + if (requested > pbz->size()) + requested = pbz->size(); + if (pbz->request_data(requested)) + { + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_ERROR_BIT); + blks.cv.notify_all(); + for (int i = 0; i < nthread; i++) + { + threads[i].join(); + } + return -1; + } + } + + uint16_t *header = (uint16_t *)p1++; + if (*header == 0x5a42) //"BZ" + { + uint32_t *magic1 = (uint32_t *)&pbz->at(i+4); + if (*magic1 == 0x26594131) //PI 3.1415926 + { + uint16_t *magic2 = (uint16_t *)&pbz->at(i + 8); + if (*magic2 == 0x5953) + { + /*which is valude bz2 header*/ + struct bz2_blk one; + memset(&one, 0, sizeof(one)); + + one.start = i; + { + lock_guard lock(blks.blk_mutex); + + blks.blk.back().size = i - blks.blk.back().start; + one.decompress_offset = blks.blk.back().decompress_offset + blks.blk.back().decompress_size; + one.decompress_size = (pbz->at(i + 3) - '0') * 100 * 1000; /* not l024 for bz2 */ + + blks.blk.push_back(one); + } + total += one.decompress_size; + + blks.cv.notify_all(); + } + } + } + } + + if (blks.blk.size() == 1) { + set_last_err_string("Can't find validate bz2 magic number"); + return -1; + } + + blks.blk.back().size = pbz->size() - blks.blk.back().start; + + { + lock_guard lock(p->m_data_mutex); + if(p->resize(total)) + return -1; + } + + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_KNOWN_SIZE); + p->m_request_cv.notify_all(); + + { + lock_guard lock(blks.blk_mutex); + struct bz2_blk one; + memset(&one, 0, sizeof(one)); + blks.blk.push_back(one); + blks.bottom = blks.blk.size(); + blks.cv.notify_all(); + } + + for (int i = 0; i < nthread; i++) + { + threads[i].join(); + } + + for (int i = 1; i < blks.blk.size(); i++) + { + if (blks.blk[i].error) + { + set_last_err_string("decompress err"); + return -1; + } + if ((blks.blk[i].decompress_size != blks.blk[i].actual_size) && (i != blks.blk.size() - 2)) /*two dummy blks (one header and other end)*/ + { + set_last_err_string("bz2: only support last block less then other block"); + return -1; + } + } + + bz2_update_available(p, &blks); + + if(p->resize(p->m_avaible_size)) + return -1; + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + + return 0; +} + +bool is_pbzip2_file(string filename) +{ + shared_ptr file=get_file_buffer(filename, true); + uint64_t filesize= file->size(); + uint64_t readsize= (filesize< (1024*1024) )? filesize:(1024*1024); //read at most 1MB, because maximum block size is 900kb + + file->request_data(readsize); + + int header_num=0; + uint8_t* ptr= file->data(); + for(size_t i =0 ; i < readsize ; i++) + { + if(ptr[0]=='B'&& ptr[1]=='Z'&& ptr[2]=='h' && ptr[4]=='1'&& ptr[5]=='A' && ptr[6]=='Y' && ptr[7]=='&' && ptr[8]=='S'&& ptr[9]=='Y') + { + header_num++; + } + ptr++; + } + if(header_num>1) + return true; + else + return false; +} + +int decompress_single_thread(string name,shared_ptrp) +{ + uint8_t* decompressed_file; + uint64_t decompressed_size; + + uint8_t* compressed_file; + uint64_t compressed_size; + + shared_ptr filebuffer=get_file_buffer(name); + + compressed_file=filebuffer->data(); + compressed_size=filebuffer->size(); + + decompressed_file=p->data(); + decompressed_size=0; + + p->reserve(7*compressed_size);//the usual compressed ratio is about 18%, so 7*18% > 100% + + bz_stream strm; + strm.bzalloc = nullptr; + strm.bzfree = nullptr; + strm.opaque = nullptr; + + int ret; + ret = BZ2_bzDecompressInit (&strm,0, 0 ); + if (ret != BZ_OK) + return -1; + strm.next_in = (char*)compressed_file; + strm.avail_in = compressed_size; + + uint64_t decompress_amount=5000; //decompress 5000 byte every iteration, choose 5000 only because the pbzip2 also used 5000 in their implementation. + while(1) + { + p->reserve(decompressed_size+1000*decompress_amount);//make sure the space is enough,multiple by 1000 to avoid repeated realloc + strm.next_out=(char*)p->data()+decompressed_size; + strm.avail_out=decompress_amount; + + ret=BZ2_bzDecompress(&strm); + decompressed_size+=decompress_amount; + + if(ret==BZ_STREAM_END) + { + decompressed_size-= strm.avail_out; + break; + } + else if (ret != BZ_OK)//if it is not bz_ok nor bz_stream_end, decompression failed. + return -1; + + } + p->resize(decompressed_size); + BZ2_bzDecompressEnd(&strm); + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + return 0; +} + + +int FSBz2::load(const string &backfile, const string &filename, shared_ptrp, bool async) +{ + if (!g_fs_data.exist(backfile)) + { + string str; + str = "Failure open file:"; + str += backfile; + set_last_err_string(str); + return -1; + } + if (filename != "*") + { + string star ("/*"); + string decompressed_name= backfile+ star; + shared_ptr decompressed_file=get_file_buffer(decompressed_name); + Tar tar; + tar.Open(decompressed_name); + if (tar.get_file_buff(filename, p)) + return -1; + p->m_avaible_size = p->m_DataSize; + } + else + { + if(!check_file_exist(backfile.substr(1))) + return -1; + if(is_pbzip2_file(backfile.substr(1))==true)//the bz2 file can be decompressed with multithreading + p->m_aync_thread = thread(bz_async_load, backfile, p); + else//the bz2 file can only be decompressed using single thread + p->m_aync_thread = thread(decompress_single_thread, backfile, p); + + if (!async) { + p->m_aync_thread.join(); + if (! p->IsLoaded()) { + set_last_err_string("async data load failure\n"); + return -1; + } + } + } + return 0; +} + +int FSGz::load(const string &backfile, const string &filename, shared_ptrp, bool async) +{ + if (!g_fs_data.exist(backfile)) + { + string str; + str = "Failure open file:"; + str += backfile; + set_last_err_string(str); + return -1; + } + if (filename != "*") + { + string star("/*"); + string decompressed_name = backfile + star; + shared_ptr decompressed_file = get_file_buffer(decompressed_name); + Tar tar; + tar.Open(decompressed_name); + if (tar.get_file_buff(filename, p)) + return -1; + p->m_avaible_size = p->m_DataSize; + } + else + { + gzFile fp = gzopen(backfile.c_str() + 1, "r"); + if (fp == nullptr) + { + set_last_err_string("Open file failure"); + return -1; + } + + shared_ptr pb = get_file_buffer(backfile); + + p->reserve(pb->size() * 4); /* guest uncompress size */ + + size_t sz = 0x100000; + if (sz > pb->size() * 4) + sz = p->size(); + + uuu_notify ut; + ut.type = uuu_notify::NOTIFY_DECOMPRESS_START; + ut.str = (char*)backfile.c_str(); + call_notify(ut); + + ut.type = uuu_notify::NOTIFY_DECOMPRESS_SIZE; + ut.total = pb->size(); + call_notify(ut); + + size_t cur = 0; + while (!gzeof(fp)) + { + size_t ret = gzread(fp, p->data() + cur, sz); + if (sz < 0) + { + set_last_err_string("decompress error"); + return -1; + } + cur += ret; + p->reserve(cur + sz); + + ut.type = uuu_notify::NOTIFY_DECOMPRESS_POS; + ut.index = gzoffset(fp); + call_notify(ut); + } + + p->resize(cur); + p->m_avaible_size = cur; + + ut.type = uuu_notify::NOTIFY_DECOMPRESS_POS; + ut.index = pb->size(); + call_notify(ut); + + gzclose(fp); + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + } + return 0; +} + +uint64_t get_file_timesample(string filename) +{ + uint64_t time=0; + g_fs_data.get_file_timesample(filename, &time); + return time; +} + +shared_ptr get_file_buffer(string filename, bool async) +{ + filename = remove_quota(filename); + + if (!filename.empty() && filename[0] != MAGIC_PATH) + { + if (filename == "..") + filename = g_current_dir.substr(0, g_current_dir.size() - 1); + else + filename = g_current_dir + filename; + } + + string_ex path; + path += filename; + + path.replace('\\', '/'); + + filename = path; + + bool find; + { + std::lock_guard lock(g_mutex_map); + find = (g_filebuffer_map.find(filename) == g_filebuffer_map.end()); + } + + if (find) + { + shared_ptr p(new FileBuffer); + + if (p->reload(filename, async)) + return nullptr; + + { + std::lock_guard lock(g_mutex_map); + g_filebuffer_map[filename] = p; + } + return p; + } + else + { + shared_ptr p; + { + std::lock_guard lock(g_mutex_map); + p= g_filebuffer_map[filename]; + } + if (p->m_timesample != get_file_timesample(filename)) + if (p->reload(filename, async)) + { + return nullptr; + } + + if (!p->IsLoaded() && !async) + { + std::lock_guard lock(p->m_async_mutex); + + if(p->m_aync_thread.joinable()) + p->m_aync_thread.join(); + + if(!p->IsLoaded()) + { + return nullptr; + } + } + + return p; + } +} + +FileBuffer::FileBuffer() +{ + m_pDatabuffer = nullptr; + m_DataSize = 0; + m_MemSize = 0; + m_dataflags = 0; + m_avaible_size = 0; +} + +FileBuffer::FileBuffer(void *p, size_t sz) +{ + m_pDatabuffer = nullptr; + m_DataSize = 0; + m_MemSize = 0; + + m_pDatabuffer = (uint8_t*)malloc(sz); + m_MemSize = m_DataSize = sz; + + memcpy(m_pDatabuffer, p, sz); + m_dataflags = 0; +} + +FileBuffer::~FileBuffer() +{ + if(m_aync_thread.joinable()) + m_aync_thread.join(); + + if (m_pDatabuffer) + { + if(m_allocate_way == ALLOCATION_WAYS::MMAP) + unmapfile(); + if(m_allocate_way == ALLOCATION_WAYS::MALLOC) + free(m_pDatabuffer); + } +} + +int FileBuffer::mapfile(string filename, size_t sz) +{ +#ifdef _MSC_VER + + m_Request.StructureVersion = REQUEST_OPLOCK_CURRENT_VERSION; + m_Request.StructureLength = sizeof(REQUEST_OPLOCK_INPUT_BUFFER); + m_Request.RequestedOplockLevel = (OPLOCK_LEVEL_CACHE_READ | OPLOCK_LEVEL_CACHE_HANDLE); + m_Request.Flags = REQUEST_OPLOCK_INPUT_FLAG_REQUEST; + + REQUEST_OPLOCK_OUTPUT_BUFFER Response; + + m_OverLapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + ResetEvent(m_OverLapped.hEvent); + + m_file_handle = CreateFile(filename.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_OVERLAPPED, + nullptr); + + if (m_file_handle == INVALID_HANDLE_VALUE) + { + string err = "Create File Failure "; + err += filename; + set_last_err_string(err); + return -1; + } + + BOOL bSuccess = DeviceIoControl(m_file_handle, + FSCTL_REQUEST_OPLOCK, + &m_Request, + sizeof(m_Request), + &Response, + sizeof(Response), + nullptr, + &m_OverLapped); + + if (bSuccess || GetLastError() == ERROR_IO_PENDING) + { + m_file_monitor = thread(file_overwrite_monitor, filename, this); + } + + m_file_map = CreateFileMapping(m_file_handle, + nullptr, PAGE_READONLY, 0, 0, nullptr); + + if (m_file_map == INVALID_HANDLE_VALUE) + { + set_last_err_string("Fail create Map"); + return -1; + } + + m_pDatabuffer = (uint8_t *)MapViewOfFile(m_file_map, FILE_MAP_READ, 0, 0, sz); + m_DataSize = sz; + m_MemSize = sz; + m_allocate_way = ALLOCATION_WAYS::MMAP; + +#else + int fd = open(filename.c_str(), O_RDONLY); + if (fd == -1) + { + string err; + err += "xx Failure open file: "; + err + filename; + set_last_err_string(err); + return -1; + } + + m_pDatabuffer = (uint8_t *)mmap64(0, sz, PROT_READ, MAP_SHARED, fd, 0); + if (m_pDatabuffer == MAP_FAILED) { + m_pDatabuffer = nullptr; + set_last_err_string("mmap failure\n"); + return -1; + } + m_DataSize = sz; + m_MemSize = sz; + m_allocate_way = ALLOCATION_WAYS::MMAP; + close(fd); +#endif + if (m_pDatabuffer) + return 0; + + set_last_err_string("mmap file failure"); + return -1; +} + +int FileBuffer::ref_other_buffer(shared_ptr p, size_t offset, size_t size) +{ + m_pDatabuffer = p->data() + offset; + m_DataSize = m_MemSize = size; + m_avaible_size = m_DataSize; + m_allocate_way = ALLOCATION_WAYS::REF; + m_ref = p; + return 0; +} + +int FileBuffer::reload(string filename, bool async) +{ + atomic_init(&this->m_dataflags, 0); + + if (g_fs_data.load(filename, shared_from_this(), async) == 0) + { + m_timesample = get_file_timesample(filename); + return 0; + } + return -1; +} + +int FileBuffer::request_data(size_t sz) +{ + std::unique_lock lck(m_requext_cv_mutex); + + while(!(this->m_dataflags & FILEBUFFER_FLAG_KNOWN_SIZE_BIT)) + m_request_cv.wait(lck); + + if (IsLoaded()) + { + if (sz > this->size()) + { + set_last_err_string("exceed data size"); + return -1; + } + } + + + while ((sz > m_avaible_size) && !IsLoaded()) + { + if (IsError()) + { + set_last_err_string("Async request data error"); + return -1; + } + m_request_cv.wait(lck); + } + + if (IsLoaded()) + { + if (sz > m_avaible_size) + { + set_last_err_string("request offset execeed memory size"); + return -1; + } + } + + return 0; +} + +int FileBuffer::request_data(std::vector &data, size_t offset, size_t sz) +{ + int64_t ret; + ret = request_data(data.data(), offset, sz); + if (ret < 0) + { + data.clear(); + return -1; + } + + data.resize(ret); + return 0; +} + +int64_t FileBuffer::request_data(void *data, size_t offset, size_t sz) +{ + bool needlock = false; + int ret = 0; + + if (IsLoaded()) + { + if (offset >= this->size()) + { + set_last_err_string("request offset execeed memory size"); + return -1; + } + } + else + { + std::unique_lock lck(m_requext_cv_mutex); + while ((offset + sz > m_avaible_size) && !IsLoaded()) + { + if (IsError()) + { + set_last_err_string("Async request data error"); + return -1; + } + m_request_cv.wait(lck); + } + + if (IsLoaded()) + { + if (offset > m_avaible_size) + { + set_last_err_string("request offset execeed memory size"); + return -1; + } + } + needlock = true; + } + + size_t size = sz; + if (offset + size >= m_avaible_size) + size = m_avaible_size - offset; + + if (needlock) m_data_mutex.lock(); + + if (this->data()) + { + memcpy(data, this->data() + offset, size); + ret = size; + } + else + { + set_last_err_string("Out of memory"); + ret = ERR_OUT_MEMORY; + } + if (needlock) m_data_mutex.unlock(); + + return ret; +} + +std::shared_ptr FileBuffer::request_data(size_t offset, size_t sz) +{ + shared_ptr p(new FileBuffer); + + if (IsLoaded()) + { + if (offset >= this->size()) + { + set_last_err_string("request offset bigger than file size"); + return nullptr; + } + + size_t size = sz; + if (offset + sz > this->size()) + size = this->size() - offset; + p->ref_other_buffer(shared_from_this(), offset, size); + return p; + } + + p->reserve(sz); + int64_t ret = request_data(p->m_pDatabuffer, offset, sz); + if (ret < 0) + return nullptr; + + p->resize(ret); + p->m_avaible_size = ret; + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + return p; +} + +int FileBuffer::reserve(size_t sz) +{ + assert(m_allocate_way == ALLOCATION_WAYS::MALLOC); + + if (sz > m_MemSize) + { + m_pDatabuffer = (uint8_t*)realloc(m_pDatabuffer, sz); + m_MemSize = sz; + + if (m_pDatabuffer == nullptr) + { + set_last_err_string("Out of memory\n"); + return -1; + } + } + + return 0; +} + +int FileBuffer::resize(size_t sz) +{ + if (this->m_allocate_way == ALLOCATION_WAYS::REF) + { + if (sz > m_DataSize) + { + assert(true); + return 0; + } + m_DataSize = sz; + return m_DataSize; + } + int ret = reserve(sz); + + m_DataSize = sz; + return ret; +} + +int FileBuffer::swap(FileBuffer &a) +{ + std::swap(m_pDatabuffer, a.m_pDatabuffer); + std::swap(m_DataSize, a.m_DataSize); + std::swap(m_MemSize, a.m_MemSize); + std::swap(m_allocate_way, a.m_allocate_way); + + return 0; +} + +int FileBuffer::unmapfile() +{ + if (m_pDatabuffer) + { +#ifdef _MSC_VER + UnmapViewOfFile(m_pDatabuffer); + m_pDatabuffer = nullptr; + CloseHandle(m_file_map); + CloseHandle(m_file_handle); + SetEvent(m_OverLapped.hEvent); + + if (m_file_monitor.joinable()) + m_file_monitor.join(); + + CloseHandle(m_OverLapped.hEvent); + m_OverLapped.hEvent = m_file_map = m_file_handle = INVALID_HANDLE_VALUE; +#else + munmap(m_pDatabuffer, m_DataSize); +#endif + m_pDatabuffer = nullptr; + } + return 0; +} + +bool check_file_exist(string filename, bool start_async_load) +{ + return get_file_buffer(filename, true) != nullptr; +} + +#ifdef WIN32 + +int file_overwrite_monitor(string filename, FileBuffer *p) +{ + WaitForSingleObject(p->m_OverLapped.hEvent, INFINITE); + + string str; + str = ">"; + str += filename; + + if(p->m_pDatabuffer && p->get_m_allocate_way() == FileBuffer::ALLOCATION_WAYS::MMAP) + { + std::lock_guard lock(g_mutex_map); + p->m_file_monitor.detach(); /*Detach itself, erase will delete p*/ + if(g_filebuffer_map.find(str) != g_filebuffer_map.end()) + g_filebuffer_map.erase(str); + } + + return 0; +} +#endif + +int uuu_for_each_ls_file(uuu_ls_file fn, const char *file_path, void *p) +{ + string_ex path; + path +=">"; + + string f = file_path; + + if(f.size() == 0) + { + path += "./"; + }else if( f[0] == '/') + { + path += "//"; + }else + { + path += "./"; + } + + path+=file_path; + path.replace('\\', '/'); + + f = path; + return g_fs_data.for_each_ls(fn, f, p); +} diff --git a/libuuu/buffer.h b/libuuu/buffer.h new file mode 100644 index 0000000..797667a --- /dev/null +++ b/libuuu/buffer.h @@ -0,0 +1,174 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#include +#else +#include +#include +#include +#endif + +#ifdef __APPLE__ +#define mmap64 mmap +#endif + +#ifdef WIN32 +class FileBuffer; +int file_overwrite_monitor(std::string filename, FileBuffer *p); +#endif + +//bit 0, data loaded +//bit 1, data total size known +#define FILEBUFFER_FLAG_LOADED_BIT 0x1 +#define FILEBUFFER_FLAG_KNOWN_SIZE_BIT 0x2 +#define FILEBUFFER_FLAG_ERROR_BIT 0x4 + +#define FILEBUFFER_FLAG_LOADED (FILEBUFFER_FLAG_LOADED_BIT|FILEBUFFER_FLAG_KNOWN_SIZE_BIT) // LOADED must be knownsize +#define FILEBUFFER_FLAG_KNOWN_SIZE FILEBUFFER_FLAG_KNOWN_SIZE_BIT + +class FileBuffer: public std::enable_shared_from_this +{ +public: + enum class ALLOCATION_WAYS + { + MALLOC, + MMAP, + REF, + VMALLOC, + }; + + std::mutex m_data_mutex; + + uint8_t *m_pDatabuffer; + size_t m_DataSize; + size_t m_MemSize; + + std::shared_ptr m_ref; + + int ref_other_buffer(std::shared_ptr p, size_t offset, size_t size); + + std::mutex m_async_mutex; + + std::atomic_int m_dataflags; + + std::thread m_aync_thread; + + std::atomic_size_t m_avaible_size; + std::condition_variable m_request_cv; + std::mutex m_requext_cv_mutex; + +#ifdef WIN32 + OVERLAPPED m_OverLapped; + REQUEST_OPLOCK_INPUT_BUFFER m_Request; + HANDLE m_file_handle; + HANDLE m_file_map; + std::thread m_file_monitor; +#endif + + FileBuffer(); + FileBuffer(void*p, size_t sz); + ~FileBuffer(); + + ALLOCATION_WAYS get_m_allocate_way() const noexcept { return m_allocate_way; } + int64_t request_data(void * data, size_t offset, size_t sz); + int request_data(std::vector &data, size_t offset, size_t sz); + int request_data(size_t total); + + std::shared_ptr request_data(size_t offset, size_t sz); + + bool IsLoaded() const noexcept + { + return m_dataflags & FILEBUFFER_FLAG_LOADED_BIT; + } + + bool IsKnownSize() const noexcept + { + return m_dataflags & FILEBUFFER_FLAG_KNOWN_SIZE_BIT; + } + + bool IsError() const noexcept + { + return m_dataflags & FILEBUFFER_FLAG_ERROR_BIT; + } + uint8_t * data() noexcept + { + return m_pDatabuffer ; + } + + size_t size() const noexcept + { + return m_DataSize; + } + + uint8_t & operator[] (size_t index) + { + assert(m_pDatabuffer); + assert(index < m_DataSize); + + return *(m_pDatabuffer + index); + } + + uint8_t & at(size_t index) + { + return (*this)[index]; + } + int resize(size_t sz); + + int reserve(size_t sz); + + int swap(FileBuffer & a); + + int mapfile(std::string filename, size_t sz); + + int unmapfile(); + //Read write lock; + uint64_t m_timesample; + int reload(std::string filename, bool async=false); + +private: + ALLOCATION_WAYS m_allocate_way = ALLOCATION_WAYS::MALLOC; +}; + +std::shared_ptr get_file_buffer(std::string filename, bool aysnc=false); +bool check_file_exist(std::string filename, bool start_async_load=true); + +void set_current_dir(const std::string &dir); diff --git a/libuuu/cmd.cpp b/libuuu/cmd.cpp new file mode 100644 index 0000000..c03f6f9 --- /dev/null +++ b/libuuu/cmd.cpp @@ -0,0 +1,1167 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include +#include +#include +#include +#include "cmd.h" +#include "libcomm.h" +#include "liberror.h" +#include "libuuu.h" +#include "config.h" +#include "trans.h" +#include "sdps.h" +#include +#include "buffer.h" +#include "sdp.h" +#include "fastboot.h" +#include +#include + +#include +#include + +static CmdMap g_cmd_map; +static CmdObjCreateMap g_cmd_create_map; +static string g_cmd_list_file; + +static map> g_environment; + +int insert_env_variable(string key, string value) +{ + g_environment[std::this_thread::get_id()][key] = value; + return 0; +} + +string get_env_variable(string key) +{ + return g_environment[std::this_thread::get_id()][key]; +} + +int clear_env() +{ + return g_environment.erase(std::this_thread::get_id()); +} + +bool is_env_exist(string key) +{ + return g_environment[std::this_thread::get_id()].find(key) != g_environment[std::this_thread::get_id()].end(); +} + +int get_string_in_square_brackets(const std::string &cmd, std::string &context); +int parser_cmd_list_file(shared_ptr pbuff, CmdMap *pCmdMap = nullptr); +std::string remove_square_brackets(const std::string &cmd); + +template +void * create_object() { return new T; } + +typedef void * (*FN)(); + +FN g_fn = create_object; + +CmdCtx::~CmdCtx() +{ +} + +CmdBase::~CmdBase() +{ +} + +int CmdBase::parser(char *p) +{ + if (p != nullptr) + m_cmd = p; + + size_t pos = 0; + string param = get_next_param(m_cmd, pos); + + if (param.find(':') != string::npos) + param = get_next_param(m_cmd, pos); + + int index = 0; + + while (pos < m_cmd.size()) + { + param = get_next_param(m_cmd, pos); + + struct Param *pp = nullptr; + + if (m_NoKeyParam) + { + if (index > m_param.size()) + { + set_last_err_string("More parameter then expected"); + return -1; + } + pp = &(m_param[index]); + index++; + } + else + { + for (size_t i = 0; i < m_param.size(); i++) + { + string key = string(m_param[i].key); + if (compare_str(param, key, m_param[i].ignore_case)) + { + pp = &(m_param[i]); + break; + } + } + } + + if (pp == nullptr) + { + string err; + err = "unknown Option"; + err += param; + set_last_err_string(err); + return -1; + } + + if (pp->type == Param::Type::e_uint32) + { + if (!m_NoKeyParam) + param = get_next_param(m_cmd, pos); + *(uint32_t*)pp->pData = str_to_uint32(param); + } + + if (pp->type == Param::Type::e_uint64) + { + if (!m_NoKeyParam) + param = get_next_param(m_cmd, pos); + *(uint64_t*)pp->pData = str_to_uint64(param); + } + + if (pp->type == Param::Type::e_string_filename) + { + if (!m_NoKeyParam) + param = get_next_param(m_cmd, pos); + *(string*)pp->pData = param; + + if (!check_file_exist(param)) + return -1; + } + + if (pp->type == Param::Type::e_string) + { + if (!m_NoKeyParam) + param = get_next_param(m_cmd, pos); + *(string*)pp->pData = param; + } + + if (pp->type == Param::Type::e_bool) + { + *(bool*)pp->pData = true; + } + + if (pp->type == Param::Type::e_null) + { + } + } + + if (m_bCheckTotalParam) + { + if (index < m_param.size()) + { + string str; + str += "Missed: "; + str += m_param[index].Error; + set_last_err_string(str); + return -1; + } + } + return 0; +} + +int CmdBase::parser_protocal(char *p, size_t &pos) +{ + if (p) + m_cmd = *p; + + string prot = get_next_param(m_cmd, pos, ':'); + string param; + if (get_string_in_square_brackets(prot, param)) + return -1; + + if (!param.empty()) + { + size_t param_pos = 0; + string s = get_next_param(param, param_pos); + + if (s == "-t") + { + string timeout; + timeout = get_next_param(param, param_pos); + m_timeout = str_to_uint32(timeout); + } + else + { + string err; + err = "Unknown option: "; + err += s; + err += " for protocol: "; + err += remove_square_brackets(prot); + set_last_err_string(err); + return -1; + } + } + return 0; +} + +int CmdBase::dump() +{ + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_CMD_INFO; + + string str = m_cmd; + str += "\n"; + nt.str = (char*)str.c_str(); + call_notify(nt); + + return 0; +} + +int CmdList::run_all(CmdCtx *p, bool dry) +{ + CmdList::iterator it; + int ret; + + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_CMD_TOTAL; + nt.total = size(); + call_notify(nt); + + int i = 0; + + for (it = begin(); it != end(); it++, i++) + { + uuu_notify nt; + + nt.type = uuu_notify::NOTIFY_CMD_INDEX; + nt.index = i; + call_notify(nt); + + nt.type = uuu_notify::NOTIFY_CMD_START; + nt.str = (char *)(*it)->get_cmd().c_str(); + call_notify(nt); + + if (dry) + ret = (*it)->dump(); + else + ret = (*it)->run(p); + + nt.type = uuu_notify::NOTIFY_CMD_END; + nt.status = ret; + call_notify(nt); + if (ret) + return ret; + + if ((*it)->get_lastcmd()) + break; + } + return ret; +} + +int CmdMap::run_all(const std::string &protocol, CmdCtx *p, bool dry_run) +{ + if (find(protocol) == end()) + { + set_last_err_id(-1); + std::string err; + err.append("Unknown Protocol:"); + err.append(protocol); + set_last_err_string(err); + return -1; + } + return at(protocol)->run_all(p, dry_run); +} + +string get_next_param(const string &cmd, size_t &pos, char sperate) +{ + string str; + if (pos == string::npos) + return str; + if (pos >= cmd.size()) + return str; + + //trim left space + while (cmd[pos] == sperate && pos < cmd.size()) + pos++; + + bool quate = false; + size_t end = string::npos; + + for (size_t s = pos; s < cmd.size(); s++) + { + if (cmd[s] == '"') + quate = !quate; + + if (!quate && cmd[s] == sperate) + { + end = s; + break; + } + } + + if (end == cmd.npos) + end = cmd.size(); + + str = cmd.substr(pos, end - pos); + pos = end + 1; + + return str; +} + +string remove_square_brackets(const string &cmd) +{ + size_t sz=cmd.find('['); + return cmd.substr(0, sz); +} + +int get_string_in_square_brackets(const string &cmd, string &context) +{ + size_t start = cmd.find('['); + if (start == string::npos) + { + context.clear(); + return 0; + } + + size_t end = cmd.find(']', start); + if (end == string::npos) + { + set_last_err_string("missed ]"); + return -1; + } + + context = cmd.substr(start + 1, end - start - 1); + return 0; +} + +template +T str_to_uint(const std::string &str, bool * conversion_succeeded) +{ + if (conversion_succeeded) *conversion_succeeded = false; + + int base = 10; + if (str.size() > 2) + { + if (str.substr(0, 2).compare("0x") == 0) + { + base = 16; + } + } + + try { + const auto tmp_val = std::stoull(str, nullptr, base); + if (tmp_val <= MAX_VAL) + { + if (conversion_succeeded) *conversion_succeeded = true; + return static_cast(tmp_val); + } + } catch (const std::invalid_argument &) { + } catch (const std::out_of_range &) { + } + + set_last_err_string("Conversion of string to unsigned failed"); + + return MAX_VAL; +} + +uint16_t str_to_uint16(const string &str, bool * conversion_suceeded) +{ + return str_to_uint(str, conversion_suceeded); +} + +uint32_t str_to_uint32(const string &str, bool * conversion_suceeded) +{ + return str_to_uint(str, conversion_suceeded); +} + +uint64_t str_to_uint64(const string &str, bool * conversion_suceeded) +{ + return str_to_uint(str, conversion_suceeded); +} + +template shared_ptr new_cmd_obj(char *p) +{ + return shared_ptr(new T(p)); +} + +CmdObjCreateMap::CmdObjCreateMap() +{ + (*this)["CFG:"] = new_cmd_obj; + + (*this)["SDPS:BOOT"] = new_cmd_obj; + + (*this)["SDP:DCD"] = new_cmd_obj; + (*this)["SDP:JUMP"] = new_cmd_obj; + (*this)["SDP:RDMEM"] = new_cmd_obj; + (*this)["SDP:WRMEM"] = new_cmd_obj; + (*this)["SDP:WRITE"] = new_cmd_obj; + (*this)["SDP:STATUS"] = new_cmd_obj; + (*this)["SDP:BOOT"] = new_cmd_obj; + (*this)["SDP:BLOG"] = new_cmd_obj; + + (*this)["SDPU:JUMP"] = new_cmd_obj; + (*this)["SDPU:WRITE"] = new_cmd_obj; + (*this)["SDPU:BLOG"] = new_cmd_obj; + + (*this)["SDPV:JUMP"] = new_cmd_obj; + (*this)["SDPV:WRITE"] = new_cmd_obj; + (*this)["SDPV:BLOG"] = new_cmd_obj; + + (*this)["FB:GETVAR"] = new_cmd_obj; + (*this)["FASTBOOT:GETVAR"] = new_cmd_obj; + (*this)["FB:UCMD"] = new_cmd_obj; + (*this)["FASTBOOT:UCMD"] = new_cmd_obj; + (*this)["FB:ACMD"] = new_cmd_obj; + (*this)["FASTBOOT:ACMD"] = new_cmd_obj; + (*this)["FB:DOWNLOAD"] = new_cmd_obj; + (*this)["FASTBOOT:DOWNLOAD"] = new_cmd_obj; + (*this)["FB:UPLOAD"] = new_cmd_obj; + (*this)["FASTBOOT:UPLOAD"] = new_cmd_obj; + (*this)["FB:FLASH"] = new_cmd_obj; + (*this)["FASTBOOT:FLASH"] = new_cmd_obj; + (*this)["FB:ERASE"] = new_cmd_obj; + (*this)["FASTBOOT:ERASE"] = new_cmd_obj; + (*this)["FB:REBOOT"] = new_cmd_obj; + (*this)["FASTBOOT:REBOOT"] = new_cmd_obj; + (*this)["FB:OEM"] = new_cmd_obj; + (*this)["FASTBOOT:OEM"] = new_cmd_obj; + (*this)["FB:FLASHING"] = new_cmd_obj; + (*this)["FASTBOOT:FLASHING"] = new_cmd_obj; + (*this)["FB:SET_ACTIVE"] = new_cmd_obj; + (*this)["FASTBOOT:SET_ACTIVE"] = new_cmd_obj; + (*this)["FB:CONTINUE"] = new_cmd_obj; + (*this)["FASTBOOT:CONTINUE"] = new_cmd_obj; + + (*this)["FB:UPDATE-SUPER"] = new_cmd_obj; + (*this)["FASTBOOT:UPDATE-SUPER"] = new_cmd_obj; + (*this)["FB:CREATE-LOGICAL-PARTITION"] = new_cmd_obj; + (*this)["FASTBOOT:CREATE-LOGICAL-PARTITION"] = new_cmd_obj; + (*this)["FB:DELETE-LOGICAL-PARTITION"] = new_cmd_obj; + (*this)["FASTBOOT:DELETE-LOGICAL-PARTITION"] = new_cmd_obj; + (*this)["FB:RESIZE-LOGICAL-PARTITION"] = new_cmd_obj; + (*this)["FASTBOOT:RESIZE-LOGICAL-PARTITION"] = new_cmd_obj; + + (*this)["FBK:UCMD"] = new_cmd_obj; + (*this)["FBK:ACMD"] = new_cmd_obj; + (*this)["FBK:SYNC"] = new_cmd_obj; + (*this)["FBK:UCP"] = new_cmd_obj; + + (*this)["_ALL:DONE"] = new_cmd_obj; + (*this)["_ALL:DELAY"] = new_cmd_obj; + (*this)["_ALL:SH"] = new_cmd_obj; + (*this)["_ALL:SHELL"] = new_cmd_obj; + (*this)["_ALL:<"] = new_cmd_obj; + (*this)["_ALL:@"] = new_cmd_obj; + (*this)["_ALL:ERROR"] = new_cmd_obj; + (*this)["_ALL:IF"] = new_cmd_obj; + +} + +shared_ptr create_cmd_obj(string cmd) +{ + string param; + size_t pos = 0; + param = get_next_param(cmd, pos, ':'); + param = remove_square_brackets(param); + param += ":"; + param = str_to_upper(param); + + if (g_cmd_create_map.find(param) == g_cmd_create_map.end()) + { + string s = param; + param = get_next_param(cmd, pos); + s += str_to_upper(param); + if (g_cmd_create_map.find(s) != g_cmd_create_map.end()) + return g_cmd_create_map[s]((char*)cmd.c_str()); + + string commoncmd = "_ALL:"; + commoncmd += str_to_upper(param); + if (g_cmd_create_map.find(commoncmd) != g_cmd_create_map.end()) + return g_cmd_create_map[commoncmd]((char*)cmd.c_str()); + } + else + { + return g_cmd_create_map[param]((char*)cmd.c_str()); + } + + string err; + err = "Unknown Command:"; + err += cmd; + set_last_err_string(err); + return nullptr; +} + +int uuu_run_cmd(const char * cmd, int dry) +{ + return run_cmd(nullptr, cmd, dry); +} + +int run_cmd(CmdCtx *pCtx, const char * cmd, int dry) +{ + shared_ptr p; + p = create_cmd_obj(cmd); + int ret; + + if (p == nullptr) + return -1; + + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_CMD_TOTAL; + nt.total = 1; + call_notify(nt); + + nt.type = uuu_notify::NOTIFY_CMD_START; + nt.str = (char *)p->get_cmd().c_str(); + call_notify(nt); + + if (typeid(*p) != typeid(CfgCmd)) + { + size_t pos = 0; + string c = cmd; + + string pro = get_next_param(c, pos, ':'); + pro = remove_square_brackets(pro); + pro += ":"; + + if (p->parser()) + ret = -1; + else + { + if (dry) + { + ret = p->dump(); + }else + { + CmdUsbCtx ctx; + if (pCtx == nullptr) + { + ret = ctx.look_for_match_device(pro.c_str()); + if (ret) + return ret; + + pCtx = &ctx; + } + + ret = p->run(pCtx); + } + } + } + else + { + return ret = dry? p->dump() : p->run(nullptr); + } + + nt.type = uuu_notify::NOTIFY_CMD_END; + nt.status = ret; + call_notify(nt); + + return ret; +} + +int CmdDone::run(CmdCtx *) +{ + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_DONE; + call_notify(nt); + return 0; +} + +int CmdDelay::parser(char * /*p*/) +{ + size_t pos = 0; + string param = get_next_param(m_cmd, pos); + + if (param.find(':') != string::npos) + param = get_next_param(m_cmd, pos); + + if (str_to_upper(param) != "DELAY") + { + string err = "Unknown Command:"; + err += param; + set_last_err_string(err); + return -1; + } + + string ms = get_next_param(m_cmd, pos); + m_ms = str_to_uint32(ms); + return 0; +} + +int CmdDelay::run(CmdCtx *) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(m_ms)); + return 0; +} + +int CmdError::parser(char *p) +{ + if (p) + m_cmd = p; + + size_t pos = 0; + string s; + + if (parser_protocal(p, pos)) + return -1; + + s = get_next_param(m_cmd, pos); + + m_error = m_cmd.substr(pos); + + return 0; +} + +int CmdError::run(CmdCtx *pCtx) +{ + set_last_err_string(m_error); + return -1; +} + +int CmdShell::parser(char * p) +{ + if (p) + m_cmd = p; + + size_t pos = 0; + string s; + + if (parser_protocal(p, pos)) + return -1; + + m_protocal = m_cmd.substr(0, pos); + + s = get_next_param(m_cmd, pos); + + m_dyn = (s == "<"); + + if (pos != string::npos && pos < m_cmd.size()) + m_shellcmd = m_cmd.substr(pos); + + return 0; +} + +int CmdShell::run(CmdCtx*pCtx) +{ +#ifndef WIN32 + #define _popen popen + #define _pclose pclose +#endif + FILE *pipe = _popen(m_shellcmd.c_str(), "r"); + + if (pipe == nullptr) + { + string err = "failure popen: "; + err += m_shellcmd.c_str(); + set_last_err_string(err); + return -1; + } + + string str; + str.resize(256); + while (fgets((char*)str.c_str(), str.size(), pipe)) + { + if (m_dyn) + { + string cmd; + cmd = m_protocal; + str.resize(strlen(str.c_str())); + cmd += ' '; + cmd += str; + + size_t pos = cmd.find_first_of("\r\n"); + if (pos != string::npos) + cmd = cmd.substr(0, pos); + + return run_cmd(pCtx, cmd.c_str(), 0); + } + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_CMD_INFO; + nt.str = (char*)str.c_str(); + call_notify(nt); + } + + /* Close pipe and print return value of pPipe. */ + if (feof(pipe)) + { + int ret = _pclose(pipe); + string_ex str; + str.format("\nProcess returned %d\n", ret);; + if (ret) + { + set_last_err_string(str.c_str()); + return ret; + } + } + else + { + set_last_err_string("Error: Failed to read the end of the pipe.\n"); + return -1; + } + + return 0; +} + +int CmdEnv::parser(char *p) +{ + if (p) + m_cmd = p; + + size_t pos = 0; + + if (parser_protocal(p, pos)) + return -1; + if (pos == string::npos || pos >= m_cmd.size()) + return -1; + + m_unfold_cmd = m_cmd.substr(0, pos); + m_unfold_cmd.append(" "); + + // read the '@' + get_next_param(m_cmd, pos); + + auto cmd = m_cmd.substr(pos); + + regex expr { "@[0-9a-zA-Z_]+@" }; + smatch result; + auto last_pos = static_cast(cmd).begin(); + auto cmd_end = static_cast(cmd).end(); + while (regex_search(last_pos, cmd_end, result, expr)) { + for (auto &i : result) { + string key { i.first + 1, i.second - 1 }; + auto value = [&key]() -> pair { +#ifndef WIN32 + auto ptr = getenv(key.c_str()); + if (ptr) + return {true, ptr}; + return {false, {}}; +#else + size_t len; + getenv_s(&len, nullptr, 0, key.c_str()); + if (!len) + return {false, {}}; + string value(len-1, '\0'); + getenv_s(&len, &value[0], len, key.c_str()); + return {true, value}; +#endif + }(); + if (!value.first) { + set_last_err_string("variable '" + key + "' is not defined"); + return -1; + } + auto begin = value.second.begin(); + auto end = value.second.end(); + auto pos = find_if(begin, end, [](char c){ return c == '\r' || c == '\n'; }); + m_unfold_cmd.append(&*last_pos, distance(last_pos, i.first)); + m_unfold_cmd.append(begin, pos); + last_pos = i.second; + } + } + m_unfold_cmd.append(&*last_pos); + + return 0; +} + +int CmdIf::parser(char *p) +{ + if (p) + m_cmd = p; + + string s; + + size_t pos = 0; + + if (parser_protocal(p, pos)) + return -1; + + m_protocal = m_cmd.substr(0, pos); + + if (pos == string::npos || pos >= m_cmd.size()) + return -1; + + s = get_next_param(m_cmd, pos); + if (str_to_upper(s) != "IF") + { + string err = "Unknown command: "; + err += s; + set_last_err_string(s); + return -1; + } + size_t lc = pos; + get_next_param(m_cmd, pos); + + int end = m_cmd.find("then", pos); + + if (end == string::npos) + { + set_last_err_string("missed key word: then"); + return -1; + } + + m_condtion = m_cmd.substr(lc, end - lc); + m_true_cmd = m_cmd.substr(end + 4); + return 0; +} + +void CmdIf::build_map(CmdCtx*p) +{ + string_ex s; + + s.format("0x%04X", p->m_config_item->m_vid); + insert_env_variable("@VID@", s); + + s.format("0x%04X", p->m_config_item->m_pid); + insert_env_variable("@PID@", s); + + s.format("0x%04X", p->m_current_bcd); + insert_env_variable("@BCD@", s); + + insert_env_variable("@CHIP@", p->m_config_item->m_chip); + +} + +int CmdIf::run(CmdCtx *p) +{ + string l, r; + string cmp[] = { "==", "!=", "" }; + int i = 0; + for (i = 0; !cmp[i].empty(); i++) + { + size_t pos = m_condtion.find(cmp[i], 0); + if (pos != string::npos) + { + l = m_condtion.substr(0, pos); + r = m_condtion.substr(pos + cmp[i].size() + 1); + break; + } + } + l = str_to_upper(trim(l)); + r = str_to_upper(trim(r)); + + build_map(p); + + if (is_env_exist(l)) + l = get_env_variable(l); + + if (is_env_exist(r)) + r = get_env_variable(r); + + switch (i) + { + case 0: // == + if (l != r) + return 0; + break; + case 1: // != + if (l == r) + return 0; + break; + default: + set_last_err_string("unknown if condition"); + return -1; + } + + //Pass condition check; + string cmd = m_protocal; + cmd += ' '; + cmd += this->m_true_cmd; + return run_cmd(p, cmd.c_str(), 0); +} + +int CmdEnv::run(CmdCtx *p) +{ + return run_cmd(p, m_unfold_cmd.c_str(), 0); +} + +int run_cmds(const char *procotal, CmdCtx *p) +{ + CmdMap cmdmap, *pCmdMap; + + if (!g_cmd_list_file.empty()) + { + shared_ptr pbuff = get_file_buffer(g_cmd_list_file); + if (pbuff == nullptr) + return -1; + if(parser_cmd_list_file(pbuff, &cmdmap)) + return -1; + pCmdMap = &cmdmap; + } + else + { + pCmdMap = &g_cmd_map; + } + + if (pCmdMap->find(procotal) == pCmdMap->end()) + { + return 0; + } + + return (*pCmdMap)[procotal]->run_all(p); +} + +static int insert_one_cmd(const char * cmd, CmdMap *pCmdMap) +{ + string s = cmd; + size_t pos = 0; + + string pro = get_next_param(s, pos, ':'); + pro = remove_square_brackets(pro); + pro += ":"; + + pro = str_to_upper(pro); + + shared_ptr p = create_cmd_obj(s); + if (p == nullptr) + return -1; + + if (p->parser()) + return -1; + + if (pCmdMap->find(pro) == pCmdMap->end()) + { + shared_ptr list(new CmdList); + (*pCmdMap)[pro] = list; + } + + (*pCmdMap)[pro]->push_back(p); + + return 0; +} + + +static int added_default_boot_cmd(const char *filename) +{ + string str; + + str = "SDPS: boot -f "; + str += "\""; + str += filename; + str += "\""; + + int ret = insert_one_cmd(str.c_str(), &g_cmd_map); + if (ret) return ret; + + insert_one_cmd("SDPS: done", &g_cmd_map); + + str = "SDP: boot -f "; + str += "\""; + str += filename; + str += "\""; + + ret = insert_one_cmd(str.c_str(), &g_cmd_map); + if (ret) return ret; + + insert_one_cmd("SDP: done", &g_cmd_map); + + str = "SDPU: write -f "; + str += "\""; + str += filename; + str += "\""; + str += " -offset 0x57c00"; + insert_one_cmd(str.c_str(), &g_cmd_map); + insert_one_cmd("SDPU: jump", &g_cmd_map); + insert_one_cmd("SDPU: done", &g_cmd_map); + + str = "SDPV: write -f "; + str += "\""; + str += filename; + str += "\""; + str += " -skipspl"; + insert_one_cmd(str.c_str(), &g_cmd_map); + insert_one_cmd("SDPV: jump", &g_cmd_map); + insert_one_cmd("SDPV: done", &g_cmd_map); + + return 0; +} + +int check_version(string str) +{ + int x = 0; + int ver = 0; + for (size_t i = 0; i < str.size(); i++) + { + char c = str[i]; + if (c >= '0' && c <= '9') + { + x *= 10; + x += c - '0'; + } + if (c == '.' || i == str.size()-1 || c == '\n') + { + ver <<= 12; + ver += x; + x = 0; + } + } + + int cur = uuu_get_version(); + + if (ver > cur) + { + string str; + str = "This version of uuu is too old, please download the latest one"; + set_last_err_string(str); + return -1; + } + return 0; +} + +int uuu_run_cmd_script(const char * buff, int dry) +{ + shared_ptr p(new FileBuffer((void*)buff, strlen(buff))); + + return parser_cmd_list_file(p); +} + +int parser_cmd_list_file(shared_ptr pbuff, CmdMap *pCmdMap) +{ + char uuu_version[] = "uuu_version"; + string str; + + if (pCmdMap == nullptr) + pCmdMap = &g_cmd_map; + + pCmdMap->clear(); + + for (size_t i = 0; i < pbuff->size(); i++) + { + uint8_t c = pbuff->at(i); + if (c == '\r') + continue; + + if(c != '\n') + str.push_back(c); + + if (c == '\n' || c == 0 || i == pbuff->size() - 1) + { + if (str.substr(0, strlen(uuu_version)) == uuu_version) + { + if (check_version(str.substr(strlen(uuu_version), 10))) + { + return -1; + } + }else if (str.size() > 1) + { + if (str[0] != '#') + if (insert_one_cmd(str.c_str(), pCmdMap)) + return -1; + } + str.clear(); + } + } + return 0; +} + +int uuu_auto_detect_file(const char *filename) +{ + string_ex fn; + fn += remove_quota(filename); + fn.replace('\\', '/'); + + if (fn.empty()) + fn += "./"; + + string oldfn =fn; + + fn += "/uuu.auto"; + shared_ptr buffer = get_file_buffer(fn); + if (buffer == nullptr) + { + fn.clear(); + fn += oldfn; + size_t pos = str_to_upper(fn).find("ZIP"); + if(pos == string::npos || pos != fn.size() - 3) + { + pos = str_to_upper(fn).find("SDCARD"); + if (pos == string::npos || pos != fn.size() - 6) + buffer = get_file_buffer(fn); //we don't try open a zip file here + } + + if(buffer == nullptr) + return -1; + } + + string str= "uuu_version"; + void *p1 = buffer->data(); + void *p2 = (void*)str.data(); + if (memcmp(p1, p2, str.size()) == 0) + { + size_t pos = fn.rfind('/'); + if (pos != string::npos) + set_current_dir(fn.substr(0, pos + 1)); + + g_cmd_list_file = fn.substr(pos+1); + + return parser_cmd_list_file(buffer); + } + + //flash.bin or uboot.bin + return added_default_boot_cmd(fn.c_str()); +} + +int notify_done(uuu_notify nt, void *p) +{ + if(nt.type == uuu_notify::NOTIFY_DONE) + *(std::atomic *) p = 1; + if (nt.type == uuu_notify::NOTIFY_CMD_END && nt.status) + *(std::atomic *) p = 1; + + return 0; +} +int uuu_wait_uuu_finish(int deamon, int dry) +{ + std::atomic exit; + exit = 0; + + if(dry) { + for(auto it=g_cmd_map.begin(); it != g_cmd_map.end(); it++) + { + for(auto cmd = it->second->begin(); cmd != it->second->end(); cmd++) + { + (*cmd)->dump(); + } + } + return 0; + } + + if (!deamon) + uuu_register_notify_callback(notify_done, &exit); + + if(polling_usb(exit)) + return -1; + + return 0; +} + diff --git a/libuuu/cmd.h b/libuuu/cmd.h new file mode 100644 index 0000000..8368fe6 --- /dev/null +++ b/libuuu/cmd.h @@ -0,0 +1,222 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#pragma once + +#include +#include +#include +#include + +class ConfigItem; + +std::string get_next_param(const std::string &cmd, size_t &pos, char sperate = ' '); + +class CmdCtx +{ +public: + CmdCtx() = default; + CmdCtx(const CmdCtx&) = delete; + CmdCtx& operator=(const CmdCtx&) = delete; + virtual ~CmdCtx(); + + ConfigItem *m_config_item = nullptr; + void *m_dev = nullptr; + short m_current_bcd; +}; + +class CmdUsbCtx : public CmdCtx +{ +public: + ~CmdUsbCtx() override; + int look_for_match_device(const char * procotol); +}; + +struct Param +{ + enum class Type + { + e_uint32, + e_uint64, + e_bool, + e_string, + e_null, + e_string_filename, + }; + + const char * const key; + const char * const Error; + void *pData; + const Type type; + const bool ignore_case; + Param(const char *ky, void *pD, Type tp, bool ignore = true, const char *error = nullptr) : + key{ky}, Error{error}, pData{pD}, type{tp}, ignore_case{ignore} + { + } +}; + +class CmdBase +{ +public: + CmdBase() = default; + CmdBase(char *p) { if (p) m_cmd = p; } + virtual ~CmdBase(); + + virtual int dump(); + const std::string& get_cmd() const noexcept { return m_cmd; } + bool get_lastcmd() const noexcept { return m_lastcmd; } + void insert_param_info(const char *key, void *pD, Param::Type tp, bool ignore_case = true, const char* err = nullptr) + { + m_param.emplace_back(Param{key, pD, tp, ignore_case, err}); + } + virtual int parser_protocal(char *p, size_t &pos); + virtual int parser(char *p = nullptr); + virtual int run(CmdCtx *p) = 0; + +protected: + bool m_bCheckTotalParam = false; + std::string m_cmd; + bool m_lastcmd = false; + bool m_NoKeyParam = false; + uint64_t m_timeout = 2000; + +private: + std::vector m_param; +}; + +using CreateCmdObj = std::shared_ptr (*) (char *); + +class CmdObjCreateMap:public std::map +{ +public: + CmdObjCreateMap(); +}; + +class CmdDone :public CmdBase +{ +public: + CmdDone(char *p) :CmdBase(p) { m_lastcmd = true; } + + int run(CmdCtx *p) override; +}; + +class CmdDelay :public CmdBase +{ +public: + CmdDelay(char *p) :CmdBase(p) {} + + int parser(char *p = nullptr) override; + int run(CmdCtx *p) override; + +private: + int m_ms = 0; +}; + +class CmdError : public CmdBase +{ +public: + CmdError(char *p) :CmdBase(p) {} + int parser(char *p = nullptr) override; + int run(CmdCtx *p) override; + +private: + std::string m_error; +}; + +class CmdShell : public CmdBase +{ +public: + CmdShell(char *p) : CmdBase(p) {} + + int parser(char *p = nullptr) override; + int run(CmdCtx *p) override; + +private: + bool m_dyn = false; + std::string m_protocal; + std::string m_shellcmd; +}; + +class CmdIf : public CmdBase +{ +public: + CmdIf(char *p) : CmdBase(p) {} + + int parser(char *p = nullptr) override; + int run(CmdCtx *p) override; + +private: + std::string m_condtion; + std::string m_protocal; + std::string m_true_cmd; + void build_map(CmdCtx *p); +}; + +class CmdEnv : public CmdBase +{ +public: + using CmdBase::CmdBase; + + int parser(char *p = nullptr) override; + int run(CmdCtx *p) override; + +private: + std::string m_unfold_cmd; +}; + +class CmdList : public std::vector> +{ +public: + int run_all(CmdCtx *p, bool dry_run = false); +}; + +class CmdMap : public std::map> +{ +public: + int run_all(const std::string &protocal, CmdCtx *p, bool dry_run = false); +}; + +class CfgCmd :public CmdBase +{ +public: + CfgCmd(char *cmd) :CmdBase(cmd) {} + + int parser(char * /*p*/) override { return 0; } + int run(CmdCtx *p) override; +}; + +int run_cmds(const char *procotal, CmdCtx *p); +int run_cmd(CmdCtx *pCtx, const char * cmd, int dry); + +int insert_env_variable(std::string key, std::string value); +std::string get_env_variable(std::string key); +int clear_env(); +bool is_evn_exist(std::string key); \ No newline at end of file diff --git a/libuuu/config.cpp b/libuuu/config.cpp new file mode 100644 index 0000000..13c6747 --- /dev/null +++ b/libuuu/config.cpp @@ -0,0 +1,208 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include "config.h" +#include "cmd.h" +#include "libcomm.h" +#include "liberror.h" +#include "libuuu.h" + +using namespace std; + +static Config g_config; + +constexpr uint16_t FSL_VID = 0x15A2; +constexpr uint16_t NXP_VID = 0x1FC9; +constexpr uint16_t BD_VID = 0x3016; + +Config::Config() +{ + emplace_back(ConfigItem{"SDPS:", "MX8QXP", nullptr, NXP_VID, 0x012F, 0x0002}); + emplace_back(ConfigItem{"SDPS:", "MX8QM", "MX8QXP", NXP_VID, 0x0129, 0x0002}); + emplace_back(ConfigItem{"SDPS:", "MX8DXL", "MX8QXP", NXP_VID, 0x0147}); + emplace_back(ConfigItem{"SDPS:", "MX28", nullptr, FSL_VID, 0x004f}); + emplace_back(ConfigItem{"SDPS:", "MX815", nullptr, NXP_VID, 0x013E}); + emplace_back(ConfigItem{"SDPS:", "MX865", "MX815", NXP_VID, 0x0146}); + emplace_back(ConfigItem{"SDPS:", "MX8ULP", "MX815", NXP_VID, 0x014A}); + emplace_back(ConfigItem{"SDPS:", "MX8ULP", "MX815", NXP_VID, 0x014B}); + emplace_back(ConfigItem{"SDP:", "MX7D", nullptr, FSL_VID, 0x0076}); + emplace_back(ConfigItem{"SDP:", "MX6Q", nullptr, FSL_VID, 0x0054}); + emplace_back(ConfigItem{"SDP:", "MX6D", "MX6Q", FSL_VID, 0x0061}); + emplace_back(ConfigItem{"SDP:", "MX6SL", "MX6Q", FSL_VID, 0x0063}); + emplace_back(ConfigItem{"SDP:", "MX6SX", "MX6Q", FSL_VID, 0x0071}); + emplace_back(ConfigItem{"SDP:", "MX6UL", "MX7D", FSL_VID, 0x007D}); + emplace_back(ConfigItem{"SDP:", "MX6ULL", "MX7D", FSL_VID, 0x0080}); + emplace_back(ConfigItem{"SDP:", "MX6SLL", "MX7D", NXP_VID, 0x0128}); + emplace_back(ConfigItem{"SDP:", "MX7ULP", nullptr, NXP_VID, 0x0126}); + emplace_back(ConfigItem{"SDP:", "MXRT106X", nullptr, NXP_VID, 0x0135}); + emplace_back(ConfigItem{"SDP:", "MX8MM", "MX8MQ", NXP_VID, 0x0134}); + emplace_back(ConfigItem{"SDP:", "MX8MQ", "MX8MQ", NXP_VID, 0x012B}); + emplace_back(ConfigItem{"SDPU:", "SPL", "SPL", 0x0525, 0xB4A4, 0, 0x04FF}); + emplace_back(ConfigItem{"SDPV:", "SPL1", "SPL", 0x0525, 0xB4A4, 0x0500, 0x9998}); + emplace_back(ConfigItem{"SDPV:", "SPL1", "SPL", NXP_VID, 0x0151, 0x0500, 0x9998}); + emplace_back(ConfigItem{"SDPU:", "SPL", "SPL", 0x0525, 0xB4A4, 0x9999, 0x9999}); /*old i.MX8 MQEVk use bcd 9999*/ + emplace_back(ConfigItem{"SDPU:", "SPL", "SPL", BD_VID, 0x1001, 0, 0x04FF}); + emplace_back(ConfigItem{"SDPV:", "SPL1", "SPL", BD_VID, 0x1001, 0x0500, 0x9998}); + emplace_back(ConfigItem{"FBK:", nullptr, nullptr, 0x066F, 0x9AFE}); + emplace_back(ConfigItem{"FBK:", nullptr, nullptr, 0x066F, 0x9BFF}); + emplace_back(ConfigItem{"FBK:", nullptr, nullptr, NXP_VID, 0x0153}); + emplace_back(ConfigItem{"FB:", nullptr, nullptr, 0x0525, 0xA4A5}); + emplace_back(ConfigItem{"FB:", nullptr, nullptr, 0x18D1, 0x0D02}); + emplace_back(ConfigItem{"FB:", nullptr, nullptr, BD_VID, 0x0001}); + emplace_back(ConfigItem{"FB:", nullptr, nullptr, NXP_VID, 0x0152}); +} + +int uuu_for_each_cfg(uuu_show_cfg fn, void *p) +{ + for (const auto &configItem : g_config) + { + if (fn(configItem.m_protocol.c_str(), + configItem.m_chip.c_str(), + configItem.m_compatible.c_str(), + configItem.m_vid, + configItem.m_pid, + configItem.m_bcdVerMin, + configItem.m_bcdVerMax, + p)) + return -1; + } + return 0; +} + +Config * get_config() noexcept +{ + return &g_config; +} + +ConfigItem * Config::find(uint16_t vid, uint16_t pid, uint16_t ver) +{ + for (auto it = begin(); it != end(); it++) + { + if (vid == it->m_vid && pid == it->m_pid) + { + if (ver >= it->m_bcdVerMin && ver <= it->m_bcdVerMax) + return &(*it); + } + } + return nullptr; +} + +Config Config::find(const string &pro) +{ + Config items; + for (auto it = begin(); it != end(); it++) + { + if (it->m_protocol == pro) + items.emplace_back(*it); + } + return items; +} + +int CfgCmd::run(CmdCtx *) +{ + size_t pos = 0; + string param; + + ConfigItem item; + param = get_next_param(m_cmd, pos); + + if (str_to_upper(param) == "CFG:") + param = get_next_param(m_cmd, pos); + + if (param.empty()) + { + set_last_err_string("Wrong param"); + return -1; + } + + item.m_protocol = str_to_upper(param); + + bool conversion_succeeded = false; + while (pos < m_cmd.size()) + { + param = get_next_param(m_cmd, pos); + if (param == "-pid") + { + param = get_next_param(m_cmd, pos); + item.m_pid = str_to_uint16(param, &conversion_succeeded); + if (!conversion_succeeded) return -1; + continue; + } + if (param == "-vid") + { + param = get_next_param(m_cmd, pos); + item.m_vid = str_to_uint16(param, &conversion_succeeded); + if (!conversion_succeeded) return -1; + continue; + } + if (param == "-bcdversion") + { + param = get_next_param(m_cmd, pos); + item.m_bcdVerMin = item.m_bcdVerMax = str_to_uint16(param, &conversion_succeeded); + if (!conversion_succeeded) return -1; + continue; + } + if (param == "-bcdmin") + { + param = get_next_param(m_cmd, pos); + item.m_bcdVerMin = str_to_uint16(param, &conversion_succeeded); + if (!conversion_succeeded) return -1; + continue; + } + if (param == "-bcdmax") + { + param = get_next_param(m_cmd, pos); + item.m_bcdVerMax = str_to_uint16(param, &conversion_succeeded); + if (!conversion_succeeded) return -1; + continue; + } + if (param == "-chip") + { + param = get_next_param(m_cmd, pos); + item.m_chip = param; + continue; + } + if (param == "-compatible") + { + param = get_next_param(m_cmd, pos); + item.m_compatible = param; + continue; + } + } + + ConfigItem *pItem= g_config.find(item.m_vid, item.m_pid, item.m_bcdVerMax); + if (pItem) + *pItem = item; + else + g_config.emplace_back(item); + + return 0; +} diff --git a/libuuu/config.h b/libuuu/config.h new file mode 100644 index 0000000..0ac7b74 --- /dev/null +++ b/libuuu/config.h @@ -0,0 +1,69 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#pragma once + +#include +#include +#include + +class ConfigItem +{ +public: + ConfigItem() = default; + ConfigItem(const char *pro, const char *chip, const char *comp, uint16_t vid, uint16_t pid, uint16_t verLow = 0, uint16_t verUp = UINT16_MAX) : + m_pid{pid}, m_vid{vid}, m_bcdVerMin{verLow}, m_bcdVerMax{verUp} + { + if (pro) + m_protocol = pro; + if (chip) + m_chip = chip; + if (comp) + m_compatible = comp; + } + std::string m_protocol; + std::string m_chip; + std::string m_compatible; + uint16_t m_pid = 0; + uint16_t m_vid = 0; + uint16_t m_bcdVerMin = 0; + uint16_t m_bcdVerMax = UINT16_MAX; +}; + +class Config :public std::vector +{ +public: + Config(); + ConfigItem *find(uint16_t vid, uint16_t pid, uint16_t ver); + Config find(const std::string &protocal); +}; + +Config * get_config() noexcept; diff --git a/libuuu/error.cpp b/libuuu/error.cpp new file mode 100644 index 0000000..74bcefe --- /dev/null +++ b/libuuu/error.cpp @@ -0,0 +1,77 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include "liberror.h" +#include "libuuu.h" +#include "libusb.h" + +using namespace std; + +static string g_last_error_str; +static int g_last_err_id; + +static uint32_t g_debug_level; + +int get_libusb_debug_level() noexcept +{ + return g_debug_level & 0xFFFF; +} + +void uuu_set_debug_level(uint32_t mask) +{ + g_debug_level = mask; + +#if LIBUSB_API_VERSION > 0x01000106 + libusb_set_option(nullptr, LIBUSB_OPTION_LOG_LEVEL, get_libusb_debug_level()); +#else + libusb_set_debug(nullptr, get_libusb_debug_level()); +#endif +} + +const char * uuu_get_last_err_string() +{ + return g_last_error_str.c_str(); +} + +void set_last_err_string(const string &str) +{ + g_last_error_str = str; +} + +int uuu_get_last_err() +{ + return g_last_err_id; +} + +void set_last_err_id(int id) +{ + g_last_err_id = id; +} diff --git a/libuuu/fastboot.cpp b/libuuu/fastboot.cpp new file mode 100644 index 0000000..0169fb4 --- /dev/null +++ b/libuuu/fastboot.cpp @@ -0,0 +1,965 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +/* + Android fastboot protocol define at + https://android.googlesource.com/platform/system/core/+/master/fastboot/ +*/ +#include +#include "fastboot.h" +#include "libcomm.h" +#include "cmd.h" +#include "buffer.h" +#include "liberror.h" +#include "libuuu.h" +#include +#include +#include +#include "sparse.h" +#include "ffu_format.h" +#include "libcomm.h" +#include "trans.h" +#include +#include "rominfo.h" + +int FastBoot::Transport(string cmd, void *p, size_t size, vector *input) +{ + if (m_pTrans->write((void*)cmd.data(), cmd.size())) + return -1; + + char buff[65]; + memset(buff, 0, 65); + + while ( strncmp(buff, "OKAY", 4) && strncmp(buff, "FAIL", 4)) + { + size_t actual; + memset(buff, 0, 65); + if (m_pTrans->read(buff, 64, &actual)) + return -1; + + if (strncmp(buff, "DATA",4) == 0) + { + size_t sz; + sz = strtoul(buff+4, nullptr, 16); + + if (input) + { + input->resize(sz); + size_t rz; + if (m_pTrans->read(input->data(), sz, &rz)) + return -1; + input->resize(rz); + } + else + { + if (sz > size) + sz = size; + + if (m_pTrans->write(p, sz)) + return -1; + } + }else + { + string s; + s = buff + 4; + m_info += s; + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_CMD_INFO; + nt.str = buff + 4; + call_notify(nt); + } + } + + if (strncmp(buff, "OKAY", 4) == 0) + return 0; + + set_last_err_string(m_info); + return -1; +} + +int FBGetVar::parser(char *p) +{ + if (p) + m_cmd = p; + + size_t pos = 0; + string param = get_next_param(m_cmd, pos); + + if (param.find(':') != string::npos) + param = get_next_param(m_cmd, pos); + + if (str_to_upper(param) != "GETVAR") + { + string err = "Unknown Command:"; + err += param; + set_last_err_string(err); + return -1; + } + + m_var = get_next_param(m_cmd, pos); + return 0; +} +int FBGetVar::run(CmdCtx *ctx) +{ + BulkTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + FastBoot fb(&dev); + string cmd; + cmd = "getvar:"; + cmd += m_var; + + if (fb.Transport(cmd, nullptr, 0)) + return -1; + + m_val = fb.m_info; + + string key = "@"; + key += str_to_upper(m_var); + key += "@"; + insert_env_variable(key, str_to_upper(fb.m_info)); + return 0; +} + +int FBCmd::parser(char *p) +{ + if (p) + m_cmd = p; + + size_t pos = 0; + string s; + + if (parser_protocal(p, pos)) + return -1; + + s = get_next_param(m_cmd, pos); + + if (str_to_upper(s) != str_to_upper(m_fb_cmd)) + { + string err = "Unknown command: "; + err += s; + set_last_err_string(s); + return -1; + } + + if(pos!=string::npos && pos < m_cmd.size()) + m_uboot_cmd = m_cmd.substr(pos); + return 0; +} + +int FBCmd::run(CmdCtx *ctx) +{ + BulkTrans dev{m_timeout}; + if (dev.open(ctx->m_dev)) + return -1; + + FastBoot fb(&dev); + string cmd; + cmd = m_fb_cmd; + cmd += m_separator; + cmd += m_uboot_cmd; + + if (fb.Transport(cmd, nullptr, 0)) + return -1; + + return 0; +} + +int FBPartNumber::run(CmdCtx *ctx) +{ + BulkTrans dev{m_timeout}; + if (dev.open(ctx->m_dev)) + return -1; + + FastBoot fb(&dev); + + string_ex cmd; + cmd.format("%s:%s:%08x", m_fb_cmd.c_str(), m_partition_name.c_str(), (uint32_t)m_Size); + + if (fb.Transport(cmd, nullptr, 0)) + return -1; + + return 0; +} + +int FBUpdateSuper::run(CmdCtx *ctx) +{ + BulkTrans dev{m_timeout}; + if (dev.open(ctx->m_dev)) + return -1; + + FastBoot fb(&dev); + + string_ex cmd; + cmd.format("%s:%s:%s", m_fb_cmd.c_str(), m_partition_name.c_str(), m_opt.c_str()); + + if (fb.Transport(cmd, nullptr, 0)) + return -1; + + return 0; +} + +int FBDownload::run(CmdCtx *ctx) +{ + BulkTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + FastBoot fb(&dev); + + shared_ptr buff = get_file_buffer(m_filename); + if (buff == nullptr) + return -1; + + string_ex cmd; + cmd.format("download:%08x", buff->size()); + + if (fb.Transport(cmd, buff->data(), buff->size())) + return -1; + + return 0; +} + +int FBUpload::run(CmdCtx* ctx) +{ + BulkTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + FastBoot fb(&dev); + + string_ex cmd; + cmd.format("upload"); + + std::vector buff; + if (fb.Transport(cmd, nullptr, buff.size(), &buff)) + return -1; + + std::ofstream fout(m_filename, ios::out | ios::trunc); + std::copy(buff.begin(), buff.end(), std::ostream_iterator(fout)); + fout.flush(); + fout.close(); + + return 0; +} + +int FBCopy::parser(char *p) +{ + if (p) + m_cmd = p; + + size_t pos = 0; + string s; + s = get_next_param(m_cmd, pos); + if (s.find(":") != s.npos) + s = get_next_param(m_cmd, pos); + + if ((str_to_upper(s) != "UCP")) + { + string err = "Unknown command: "; + err += s; + set_last_err_string(s); + return -1; + } + + string source; + string dest; + + source = get_next_param(m_cmd, pos); + dest = get_next_param(m_cmd, pos); + + if (source.empty()) + { + set_last_err_string("ucp: source missed"); + return -1; + } + + if (dest.empty()) + { + set_last_err_string("ucp: destination missed"); + return -1; + } + + if (source.find("T:") == 0 || source.find("t:") == 0) + { + if (dest.find("T:") == 0 || dest.find("t:") == 0) + { + set_last_err_string("ucp just support one is remote file start with t:"); + return -1; + } + m_target_file = source.substr(2); + m_bDownload = false; //upload a file + m_local_file = dest; + } + else if (dest.find("T:") == 0 || dest.find("t:") == 0) + { + m_target_file = dest.substr(2); + m_bDownload = true; + m_local_file = source; + get_file_buffer(source, true); + } + else + { + set_last_err_string("ucp must a remote file name, start with t:"); + return -1; + } + return 0; +} + +int FBCopy::run(CmdCtx *ctx) +{ + BulkTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + FastBoot fb(&dev); + string_ex cmd; + + if(m_bDownload) + { + size_t i; + shared_ptr buff = get_file_buffer(m_local_file); + if (buff == nullptr) + { + return -1; + } + + cmd.format("WOpen:%s", m_target_file.c_str()); + if (fb.Transport(cmd, nullptr, 0)) + { + if (fb.m_info == "DIR") + { + Path p; + p.append(m_local_file); + string target = m_target_file; + target += "/"; + target += p.get_file_name(); + + cmd.format("WOpen:%s", target.c_str()); + if (fb.Transport(cmd, nullptr, 0)) + return -1; + } + else { + return -1; + } + } + + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_TRANS_SIZE; + nt.total = buff->size(); + call_notify(nt); + + for (i = 0; i < buff->size(); i += this->m_Maxsize_pre_cmd) + { + size_t sz = buff->size() - i; + if (sz > m_Maxsize_pre_cmd) + sz = m_Maxsize_pre_cmd; + + cmd.format("donwload:%08X", sz); + if (fb.Transport(cmd, buff->data() + i, sz)) + { + if (fb.m_info == "EPIPE") + set_last_err_string("pipe closed by target"); + else + set_last_err_string("target return unknown error"); + + cmd.format("Close"); + if (fb.Transport(cmd, nullptr, 0)) + return -1; + + return -1; + } + + nt.type = uuu_notify::NOTIFY_TRANS_POS; + nt.index = i; + call_notify(nt); + } + + nt.type = uuu_notify::NOTIFY_TRANS_POS; + nt.index = buff->size(); + call_notify(nt); + } + else + { + cmd.format("ROpen:%s", m_target_file.c_str()); + if (fb.Transport(cmd, nullptr, 0)) + return -1; + + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_TRANS_SIZE; + size_t total = nt.total = strtoul(fb.m_info.c_str(), nullptr, 16); + call_notify(nt); + + nt.index = 0; + ofstream of; + + struct stat st; + + Path localfile; + localfile.append(m_local_file); + + if (stat(localfile.c_str(), &st) == 0) + { + if (st.st_mode & S_IFDIR) + { + localfile += "/"; + Path t; + t.append(m_target_file); + localfile += t.get_file_name(); + } + } + + of.open(localfile, ofstream::binary); + + if (!of) + { + string err; + err = "Fail to open file"; + err += localfile; + set_last_err_string(err); + } + do + { + vector data; + if (fb.Transport("upload", nullptr, 0, &data)) + return -1; + + of.write((const char*)data.data(), data.size()); + + nt.type = uuu_notify::NOTIFY_TRANS_POS; + nt.index += data.size(); + call_notify(nt); + + if (data.size() == 0) + break; + + } while (nt.index < total || total == 0 ); // If total is 0, it is stream + + nt.type = uuu_notify::NOTIFY_TRANS_POS; + call_notify(nt); + } + + cmd.format("Close"); + if (fb.Transport(cmd, nullptr, 0)) + return -1; + + return 0; +} + +int FBFlashCmd::parser(char *p) +{ + if (FBCmd::parser(p)) + return -1; + + string subcmd = m_uboot_cmd; + size_t pos = 0; + m_partition = get_next_param(subcmd, pos); + + if (m_partition == "-raw2sparse") + { + m_raw2sparse = true; + m_partition = get_next_param(subcmd, pos); + } + + if (m_partition == "-scanterm") + { + m_scanterm = true; + m_partition = get_next_param(subcmd, pos); + } + + if (m_partition == "-S") + { + m_partition = get_next_param(subcmd, pos); + bool conversion_success = false; + m_sparse_limit = str_to_uint64(m_partition, &conversion_success); + if (!conversion_success) + { + set_last_err_string("FB: flash failed to parse size argument given to -S: "s + m_partition); + return -1; + } + m_partition = get_next_param(subcmd, pos); + } + + if (m_partition == "-scanlimited") + { + m_partition = get_next_param(subcmd, pos); + bool conversion_success = false; + m_scan_limited = str_to_uint64(m_partition, &conversion_success); + if (!conversion_success) + { + set_last_err_string("FB: flash failed to parse size argument given to -scanlimited: "s + m_partition); + return -1; + } + m_partition = get_next_param(subcmd, pos); + } + if (pos == string::npos || m_partition.empty()) + { + set_last_err_string("Missed partition name"); + return -1; + } + m_filename = get_next_param(subcmd, pos); + if (m_filename.empty()) + { + set_last_err_string("Missed file name"); + return -1; + } + + if (!check_file_exist(m_filename)) + return -1; + + return 0; +} + +int FBFlashCmd::flash(FastBoot *fb, void * pdata, size_t sz) +{ + string_ex cmd; + cmd.format("download:%08x", sz); + + if (fb->Transport(cmd, pdata, sz)) + return -1; + + cmd.format("flash:%s", m_partition.c_str()); + if (fb->Transport(cmd, nullptr, 0)) + return -1; + + return 0; +} + +int FBFlashCmd::flash_raw2sparse(FastBoot *fb, shared_ptr pdata, size_t block_size, size_t max) +{ + SparseFile sf; + + vector data; + + if (max > m_sparse_limit) + max = m_sparse_limit; + + sf.init_header(block_size, (max + block_size -1) / block_size); + + data.resize(block_size); + + uuu_notify nt; + bool bload = pdata->IsKnownSize(); + + nt.type = uuu_notify::NOTIFY_TRANS_SIZE; + if (bload) + nt.total = pdata->size(); + else + nt.total = 0; + + call_notify(nt); + + + size_t i = 0; + int r; + while (!(r=pdata->request_data(data, i*block_size, block_size))) + { + int ret = sf.push_one_block(data.data()); + if (ret) + { + if (flash(fb, sf.m_data.data(), sf.m_data.size())) + return -1; + + sf.init_header(block_size, (max + block_size - 1) / block_size); + + chunk_header_t ct; + ct.chunk_type = CHUNK_TYPE_DONT_CARE; + ct.chunk_sz = i + 1; + ct.reserved1 = 0; + ct.total_sz = sizeof(ct); + + sf.push_one_chuck(&ct, nullptr); + + nt.type = uuu_notify::NOTIFY_TRANS_POS; + nt.total = i * block_size; + call_notify(nt); + } + + i++; + + if (bload != pdata->IsKnownSize()) + { + nt.type = uuu_notify::NOTIFY_TRANS_SIZE; + nt.total = pdata->size(); + call_notify(nt); + + bload = pdata->IsKnownSize(); + } + } + + if (r == ERR_OUT_MEMORY) + return r; + + if (flash(fb, sf.m_data.data(), sf.m_data.size())) + return -1; + + nt.type = uuu_notify::NOTIFY_TRANS_SIZE; + nt.total = pdata->size(); + call_notify(nt); + + nt.type = uuu_notify::NOTIFY_TRANS_POS; + nt.total = pdata->size(); + call_notify(nt); + + return 0; +} + +int FBFlashCmd::run(CmdCtx *ctx) +{ + FBGetVar getvar((char*)"FB: getvar max-download-size"); + if (getvar.parser(nullptr)) + return -1; + if (getvar.run(ctx)) + return -1; + + size_t max = getvar.m_val.empty() ? m_sparse_limit : str_to_uint32(getvar.m_val); + + BulkTrans dev{m_timeout}; + if (dev.open(ctx->m_dev)) + return -1; + + FastBoot fb(&dev); + + if (m_raw2sparse) + { + size_t block_size = 4096; + + if (getvar.parser((char*)"FB: getvar logical-block-size")) + return -1; + if (!getvar.run(ctx)) + block_size = str_to_uint32(getvar.m_val); + + if (block_size == 0) + { + set_last_err_string("Device report block_size is 0"); + return -1; + } + + shared_ptr pdata = get_file_buffer(m_filename, true); + + if (isffu(pdata)) + { + string str; + str = "FB: getvar partition-size:"; + str += m_partition; + + if (getvar.parser((char*)str.c_str())) + return -1; + + if (getvar.run(ctx)) + return -1; + + m_totalsize = str_to_uint64(getvar.m_val); + + return flash_ffu(&fb, pdata); + } + + return flash_raw2sparse(&fb, pdata, block_size, max); + } + + shared_ptr pdata = get_file_buffer(m_filename, true); + if (pdata == nullptr) + return -1; + + pdata->request_data(sizeof(sparse_header)); + if (SparseFile::is_validate_sparse_file(pdata->data(), sizeof(sparse_header))) + { /* Limited max size to 16M for sparse file to avoid long timeout at read status*/ + if (max > m_sparse_limit) + max = m_sparse_limit; + } + + if (m_scanterm) + { + pdata->request_data(m_scan_limited); + size_t length,pos=0; + if (IsMBR(pdata)) + { + length = ScanTerm(pdata, pos); + if (length == 0) + { + set_last_err_string("This wic have NOT terminate tag after bootloader, please use new yocto"); + return -1; + } + size_t offset = pos - length; + if (offset < 0) + { + set_last_err_string("This wic boot length is wrong"); + return -1; + } + return flash(&fb, pdata->data() + offset, length); + } + } + + if (pdata->size() <= max) + { + pdata->request_data(pdata->size()); + + if (flash(&fb, pdata->data(), pdata->size())) + return -1; + } + else + { + size_t pos = 0; + pdata->request_data(sizeof(sparse_header)); + sparse_header * pfile = (sparse_header *)pdata->data(); + + if (!SparseFile::is_validate_sparse_file(pdata->data(), sizeof(sparse_header))) + { + set_last_err_string("Sparse file magic miss matched"); + return -1; + } + + SparseFile sf; + size_t startblock; + chunk_header_t * pheader; + + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_TRANS_SIZE; + nt.total = pfile->total_blks; + call_notify(nt); + + sf.init_header(pfile->blk_sz, max / pfile->blk_sz); + startblock = 0; + + for(size_t nblk=0; nblk < pfile->total_chunks && pos <= pdata->size(); nblk++) + { + pdata->request_data(pos+sizeof(chunk_header_t)+sizeof(sparse_header)); + size_t oldpos = pos; + pheader = SparseFile::get_next_chunk(pdata->data(), pos); + pdata->request_data(pos); + + size_t sz = sf.push_one_chuck(pheader, pheader + 1); + if (sz == pheader->total_sz - sizeof(chunk_header_t)) + { + startblock += pheader->chunk_sz; + } + else if (sz == 0) + { + //whole chuck have not push into data. + if (flash(&fb, sf.m_data.data(), sf.m_data.size())) + return -1; + + sf.init_header(pfile->blk_sz, max / pfile->blk_sz); + + chunk_header_t ct; + ct.chunk_type = CHUNK_TYPE_DONT_CARE; + ct.chunk_sz = startblock; + ct.reserved1 = 0; + ct.total_sz = sizeof(ct); + + sz = sf.push_one_chuck(&ct, nullptr); + + /* + roll back pos to previous failure chunck and let it push again into new sparse file. + can't push it here because next chuck may big size chuck and need split as below else logic. + */ + pos = oldpos; + nblk--; + + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_TRANS_POS; + nt.total = startblock; + call_notify(nt); + } + else + { + size_t off = ((uint8_t*)pheader) - pdata->data() + sz + sizeof(chunk_header_t); + startblock += sz / pfile->blk_sz; + + do + { + if (flash(&fb, sf.m_data.data(), sf.m_data.size())) + return -1; + + sf.init_header(pfile->blk_sz, max / pfile->blk_sz); + + chunk_header_t ct; + ct.chunk_type = CHUNK_TYPE_DONT_CARE; + ct.chunk_sz = startblock; + ct.reserved1 = 0; + ct.total_sz = sizeof(ct); + + sz = sf.push_one_chuck(&ct, nullptr); + + sz = sf.push_raw_data(pdata->data() + off, pos - off); + off += sz; + startblock += sz / pfile->blk_sz; + + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_TRANS_POS; + nt.total = startblock; + call_notify(nt); + + } while (off < pos); + } + } + + //send last data + if (flash(&fb, sf.m_data.data(), sf.m_data.size())) + return -1; + + sparse_header * pf = (sparse_header *)sf.m_data.data(); + nt.type = uuu_notify::NOTIFY_TRANS_POS; + nt.total = startblock + pf->total_blks; + call_notify(nt); + } + return 0; +} + +bool FBFlashCmd::isffu(shared_ptr p) +{ + vector data; + data.resize(sizeof(FFU_SECURITY_HEADER)); + p->request_data(data, 0, sizeof(FFU_SECURITY_HEADER)); + + FFU_SECURITY_HEADER *h = (FFU_SECURITY_HEADER*)data.data(); + if (strncmp((const char*)h->signature, FFU_SECURITY_SIGNATURE, sizeof(h->signature)) == 0) + return true; + else + return false; +} + +int FBFlashCmd::flash_ffu_oneblk(FastBoot *fb, shared_ptr p, size_t off, size_t blksz, size_t blkindex) +{ + SparseFile sf; + + sf.init_header(blksz, 10); + + p->request_data(off + blksz); + + chunk_header_t ct; + ct.chunk_type = CHUNK_TYPE_DONT_CARE; + ct.chunk_sz = blkindex; + ct.reserved1 = 0; + ct.total_sz = sizeof(ct); + + sf.push_one_chuck(&ct, nullptr); + + if (sf.push_one_block(p->data() + off)) + return -1; + + return flash(fb, sf.m_data.data(), sf.m_data.size()); +} + +int FBFlashCmd::flash_ffu(FastBoot *fb, shared_ptr p) +{ + p->request_data(sizeof(FFU_SECURITY_HEADER)); + FFU_SECURITY_HEADER *h = (FFU_SECURITY_HEADER*)p->data(); + if (strncmp((const char*)h->signature, FFU_SECURITY_SIGNATURE, sizeof(h->signature)) != 0) + { + set_last_err_string("Invalidate FFU Security header signature"); + return -1; + } + + size_t off; + off = h->dwCatalogSize + h->dwHashTableSize; + off = round_up(off, (size_t)h->dwChunkSizeInKb * 1024); + + p->request_data(off + sizeof(FFU_IMAGE_HEADER)); + FFU_IMAGE_HEADER *pIh = (FFU_IMAGE_HEADER *)(p->data() + off); + + if (strncmp((const char*)pIh->Signature, FFU_SIGNATURE, sizeof(pIh->Signature)) != 0) + { + set_last_err_string("Invalidate FFU Security header signature"); + return -1; + } + + off += pIh->ManifestLength + pIh->cbSize; + off = round_up(off, (size_t)h->dwChunkSizeInKb * 1024); + + p->request_data(off + sizeof(FFU_STORE_HEADER)); + FFU_STORE_HEADER *pIs = (FFU_STORE_HEADER*) (p->data() + off); + + if(pIs->MajorVersion == 1) + off += pIs->dwValidateDescriptorLength + offsetof(FFU_STORE_HEADER, NumOfStores); + else + off += pIs->dwValidateDescriptorLength + sizeof(FFU_STORE_HEADER); + + p->request_data(off + pIs->dwWriteDescriptorLength); + + size_t block_off = off + pIs->dwWriteDescriptorLength; + block_off = round_up(block_off, (size_t)h->dwChunkSizeInKb * 1024); + + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_TRANS_SIZE; + nt.total = pIs->dwWriteDescriptorCount; + call_notify(nt); + + size_t currrent_block = 0; + size_t i; + for (i = 0; i < pIs->dwWriteDescriptorCount; i++) + { + FFU_BLOCK_DATA_ENTRY *entry = (FFU_BLOCK_DATA_ENTRY*)(p->data() + off); + + off += sizeof(FFU_BLOCK_DATA_ENTRY) + (entry->dwLocationCount -1) * sizeof(_DISK_LOCATION); + + if (currrent_block >= pIs->dwInitialTableIndex && currrent_block < pIs->dwInitialTableIndex + pIs->dwInitialTableCount) + { + //Skip Init Block + } + else + { + for (uint32_t loc = 0; loc < entry->dwLocationCount; loc++) + { + //printf("block 0x%x write to 0x%x seek %d\n", currrent_block, entry->rgDiskLocations[loc].dwBlockIndex, entry->rgDiskLocations[loc].dwDiskAccessMethod); + uint32_t access = entry->rgDiskLocations[loc].dwDiskAccessMethod; + uint32_t blockindex; + if (entry->rgDiskLocations[loc].dwDiskAccessMethod == DISK_BEGIN) + blockindex = entry->rgDiskLocations[loc].dwBlockIndex; + else + blockindex = m_totalsize / pIs->dwBlockSizeInBytes - 1 - entry->rgDiskLocations[loc].dwBlockIndex; + + for (uint32_t blk = 0; blk < entry->dwBlockCount; blk++) + { + if (flash_ffu_oneblk(fb, + p, + block_off + (currrent_block + blk) * pIs->dwBlockSizeInBytes, + pIs->dwBlockSizeInBytes, + blockindex + blk)) + return -1; + } + } + } + + nt.type = uuu_notify::NOTIFY_TRANS_POS; + nt.total = i; + call_notify(nt); + + currrent_block += entry->dwBlockCount; + } + + nt.type = uuu_notify::NOTIFY_TRANS_POS; + nt.total = i; + call_notify(nt); + + return 0; +} diff --git a/libuuu/fastboot.h b/libuuu/fastboot.h new file mode 100644 index 0000000..46c6bb5 --- /dev/null +++ b/libuuu/fastboot.h @@ -0,0 +1,270 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#pragma once + +#include "cmd.h" + +#include + +class FBFlashCmd; +class FileBuffer; +class TransBase; + +/* +Android fastboot protocol define at +https://android.googlesource.com/platform/system/core/+/master/fastboot/ +*/ + +class FastBoot +{ +public: + FastBoot(TransBase *p) : m_pTrans{p} {} + + int Transport(std::string cmd, void *p = nullptr, size_t size = 0, std::vector *input = nullptr); + int Transport(std::string cmd, std::vector data, std::vector *input = nullptr) { return Transport(cmd, data.data(), data.size(), input); } + + std::string m_info; + +private: + TransBase *const m_pTrans = nullptr; +}; + +class FBGetVar : public CmdBase +{ +public: + FBGetVar(char *p) :CmdBase(p) {} + + int parser(char *p = nullptr) override; + int run(CmdCtx *ctx) override; + +private: + std::string m_val; + std::string m_var; + + friend FBFlashCmd; +}; + +class FBCmd: public CmdBase +{ +public: + FBCmd(char *p, std::string &&fb_cmd, char separator =':') : + CmdBase(p), m_fb_cmd{std::move(fb_cmd)}, m_separator(separator) {} + + int parser(char *p = nullptr) override; + int run(CmdCtx *ctx) override; + +protected: + std::string m_uboot_cmd; + +private: + const std::string m_fb_cmd; + const char m_separator = ':'; +}; + +class FBUCmd : public FBCmd +{ +public: + FBUCmd(char *p) :FBCmd(p, "UCmd") {} +}; + +class FBACmd : public FBCmd +{ +public: + FBACmd(char *p) :FBCmd(p, "ACmd") {} +}; + +class FBSyncCmd: public FBCmd +{ +public: + FBSyncCmd(char *p) : FBCmd(p, "Sync") {} +}; + +class FBFlashingCmd : public FBCmd +{ +public: + FBFlashingCmd(char *p) : FBCmd(p, "flashing") {} +}; + +class FBOemCmd : public FBCmd +{ +public: + FBOemCmd(char *p) : FBCmd(p, "oem", ' ') {} +}; + +class FBFlashCmd : public FBCmd +{ +public: + FBFlashCmd(char *p) : FBCmd(p, "flash") { m_timeout = 10000; } + int parser(char *p = nullptr) override; + int run(CmdCtx *ctx) override; + int flash(FastBoot *fb, void *p, size_t sz); + int flash_raw2sparse(FastBoot *fb, std::shared_ptr p, size_t blksz, size_t max); + bool isffu(std::shared_ptr p); + int flash_ffu(FastBoot *fb, std::shared_ptr p); + int flash_ffu_oneblk(FastBoot *fb, std::shared_ptr p, size_t off, size_t blksz, size_t blkindex); + +private: + std::string m_filename; + std::string m_partition; + bool m_raw2sparse = false; + size_t m_sparse_limit = 0x1000000; + uint64_t m_totalsize; + bool m_scanterm = false; + uint64_t m_scan_limited = UINT64_MAX; +}; + +class FBDelPartition : public FBCmd +{ +public: + FBDelPartition(char*p) : FBCmd(p, "delete-logical-partition") {} +}; + +class FBPartNumber : public CmdBase +{ +public: + FBPartNumber(char *p, std::string &&fb_cmd) :CmdBase(p), m_fb_cmd{std::move(fb_cmd)} + { + m_Size = 0; + m_bCheckTotalParam = true; + m_NoKeyParam = true; + insert_param_info(nullptr, &m_partition_name, Param::Type::e_string, false, "partition name"); + insert_param_info(nullptr, &m_Size, Param::Type::e_uint32, false, "partition size"); + } + + int run(CmdCtx *ctx) override; + +private: + const std::string m_fb_cmd; + std::string m_partition_name; + uint32_t m_Size; +}; + +class FBCreatePartition : public FBPartNumber +{ +public: + FBCreatePartition(char*p) :FBPartNumber(p, "create-logical-partition") {} +}; + +class FBResizePartition : public FBPartNumber +{ +public: + FBResizePartition(char*p) :FBPartNumber(p, "resize-logical-partition") {} +}; + +class FBUpdateSuper : public CmdBase +{ +public: + FBUpdateSuper(char *p) :CmdBase(p) + { + m_bCheckTotalParam = true; + m_NoKeyParam = true; + insert_param_info(nullptr, &m_partition_name, Param::Type::e_string, false, "partition name"); + insert_param_info(nullptr, &m_opt, Param::Type::e_string, false, "partition size"); + } + + int run(CmdCtx *ctx) override; + +private: + const std::string m_fb_cmd = "update-super"; + std::string m_opt; + std::string m_partition_name; +}; + +class FBEraseCmd : public FBCmd +{ +public: + FBEraseCmd(char *p) : FBCmd(p, "erase") {} +}; + + +class FBRebootCmd : public FBCmd +{ +public: + FBRebootCmd(char *p) : FBCmd(p, "reboot") {} +}; + + +class FBSetActiveCmd : public FBCmd +{ +public: + FBSetActiveCmd(char *p) : FBCmd(p, "set_active") {} +}; + +class FBDownload : public CmdBase +{ +public: + FBDownload(char *p) :CmdBase(p) + { + insert_param_info("download", nullptr, Param::Type::e_null); + insert_param_info("-f", &m_filename, Param::Type::e_string_filename); + } + + int run(CmdCtx *ctx) override; + +private: + std::string m_filename; +}; + +class FBCopy : public CmdBase +{ +public: + FBCopy(char *p) :CmdBase(p) {} + int parser(char *p = nullptr) override; + int run(CmdCtx *ctx) override; + +private: + bool m_bDownload; + std::string m_local_file; + size_t m_Maxsize_pre_cmd = 0x10000; + std::string m_target_file; +}; + +class FBContinueCmd : public FBCmd +{ +public: + FBContinueCmd(char *p) : FBCmd(p, "continue") {} +}; + +class FBUpload : public CmdBase +{ +public: + FBUpload(char* p) : CmdBase(p) + { + insert_param_info("upload", nullptr, Param::Type::e_null); + insert_param_info("-f", &m_filename, Param::Type::e_string); + } + + int run(CmdCtx* ctx) override; + +private: + std::string m_filename; +}; diff --git a/libuuu/fat.cpp b/libuuu/fat.cpp new file mode 100644 index 0000000..816ccf7 --- /dev/null +++ b/libuuu/fat.cpp @@ -0,0 +1,207 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include +#include +#include +#include "libcomm.h" +#include "libuuu.h" +#include "liberror.h" + +#include "fat.h" + +int Fat::Open(string filename) +{ + m_filename = filename; + + shared_ptr pbuff = get_file_buffer(m_filename); + if (pbuff == nullptr) + return -1; + if (pbuff->size() < 512) + { + set_last_err_string("File too small"); + return -1; + } + if (pbuff->at(510) != 0x55|| pbuff->at(511) != 0xAA) + { + set_last_err_string("Partition signature miss matched"); + return -1; + } + + Partition *pPart = (Partition *)(pbuff->data() + 446); + + m_fat_part_start = pPart->lba_start * 512; + + uint8_t *boot = pbuff->data() + m_fat_part_start; + if (boot[510] != 0x55 || boot[511] != 0xAA) + { + set_last_err_string("Boot Sector signature miss matched"); + return -1; + } + + m_logical_sector_perfat = boot[0x16]; + m_logical_sector_perfat += boot[0x17] << 8; + if (m_logical_sector_perfat == 0) + { + m_logical_sector_perfat = boot[0x24]; + m_logical_sector_perfat += boot[0x25] << 8; + m_logical_sector_perfat += boot[0x26] << 16; + m_logical_sector_perfat += boot[0x27] << 24; + } + + m_fat_table_offset = boot[0xE]; + m_fat_table_offset += boot[0xF] << 8; + + m_fat_table_offset *= 512; + + m_cluster = boot[0xD]; + m_cluster *= 512; + + int num_of_fat = boot[0x10]; + + m_root_dir_offset = m_logical_sector_perfat * 512 * num_of_fat + m_fat_table_offset; + + m_num_of_rootdir = boot[0x11]; + m_num_of_rootdir = boot[0x12] << 8; + + FatDirEntry *entry; + entry = (FatDirEntry*)(boot + m_root_dir_offset); + m_filemap.clear(); + + for (int i = 0; i < m_num_of_rootdir; i++) + { + string filename; + if (entry->attr == 0x8) + entry++; + + if (entry->filename[0] == 0) + break; + + filename.clear(); + + while (entry->attr == 0xF) + { + filename.insert(0, lfn2string((FatLFN *)entry)); + entry++; + } + + if (filename.empty()) + { + filename.append((char*)entry->filename, 8); + if (entry->ext[0]) + { + filename.append("."); + filename.append((char*)entry->ext, 3); + } + } + m_filemap[filename] = *entry; + entry++; + + if (entry->filename[0] == 0) + break; + } + return 0; +} + +int Fat::get_next_cluster(shared_ptr p, int cluster) +{ + uint16_t *pfat = (uint16_t*)(p->data() + m_fat_part_start + m_fat_table_offset); + return pfat[cluster]; +} + +void *Fat::get_data_buff(shared_ptr p, int cluster) +{ + void *p1 = p->data() + m_fat_part_start + m_root_dir_offset + (cluster-2) * m_cluster + m_num_of_rootdir * 32; + return p1; +} + +int Fat::get_file_buff(string filename, shared_ptrp) +{ + if (m_filemap.find(filename) == m_filemap.end()) + { + string err; + err = "Can't find file "; + err += filename; + set_last_err_string(err); + return -1; + } + + shared_ptr pbuff = get_file_buffer(m_filename); + + size_t filesize = m_filemap[filename].file_size; + p->resize(filesize); + + int cur = m_filemap[filename].start_cluster; + + size_t off; + for (off = 0; off < filesize; off += m_cluster) + { + size_t sz; + sz = filesize - off; + if (sz > m_cluster) + sz = m_cluster; + + if (cur == 0xFFFF) + { + set_last_err_string("Early finished at fat"); + return -1; + } + void *pcluster = get_data_buff(pbuff, cur); + memcpy(p->data() + off, pcluster, sz); + + cur = get_next_cluster(pbuff, cur); + } + return 0; +} + +std::string Fat::lfn2string(FatLFN *p) +{ + string str; + for (int i = 0; i < 10; i += 2) + if (p->name1[i] == 0) + return str; + else + str += p->name1[i]; + + for (int i = 0; i < 12; i += 2) + if (p->name2[i] == 0) + return str; + else + str += p->name2[i]; + + for (int i = 0; i < 4; i += 2) + if (p->name3[i] == 0) + return str; + else + str += p->name3[i]; + + return str; +} diff --git a/libuuu/fat.h b/libuuu/fat.h new file mode 100644 index 0000000..3eb3af6 --- /dev/null +++ b/libuuu/fat.h @@ -0,0 +1,103 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#pragma once + +#include "backfile.h" +#include "buffer.h" + +#include + +#pragma pack(1) +struct Partition +{ + uint8_t status; + uint8_t start_head; + uint8_t start_sector; + uint8_t start_cylinder; + uint8_t type; + uint8_t end_head; + uint8_t end_sector; + uint8_t end_cylinder; + uint32_t lba_start; + uint32_t lba_num; +}; + +struct FatDirEntry +{ + uint8_t filename[8]; + uint8_t ext[3]; + uint8_t attr; + uint8_t user_attr; + uint8_t delele_char; + uint16_t create_time; + uint16_t create_date; + uint16_t userid; + uint16_t access; + uint16_t modify_time; + uint16_t modify_date; + uint16_t start_cluster; + uint32_t file_size; +}; + +struct FatLFN +{ + uint8_t seq; + uint8_t name1[10]; + uint8_t attr; + uint8_t type; + uint8_t sum; + uint8_t name2[12]; + uint16_t start_cluster; + uint8_t name3[4]; +}; + +#pragma pack() + +class Fat : public Backfile +{ +public: + void *get_data_buff(shared_ptr p, int cluster); + int get_file_buff(string filename, shared_ptrp); + int get_next_cluster(shared_ptr p, int cluster); + string lfn2string(FatLFN *p); + int Open(string filename); + + map m_filemap; + +private: + uint64_t m_cluster; + uint64_t m_fat_part_start; + uint64_t m_fat_table_offset; + uint64_t m_logical_sector_perfat; + int m_num_of_rootdir; + uint64_t m_root_dir_offset; +}; diff --git a/libuuu/ffu_format.h b/libuuu/ffu_format.h new file mode 100644 index 0000000..523400a --- /dev/null +++ b/libuuu/ffu_format.h @@ -0,0 +1,115 @@ +/* + * Copyright 2020 NXP. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of the NXP Semiconductor nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +//ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/mobile/ffu-image-format + +#ifndef LIBSPARSE_FFU_FORMAT_H +#define LIBSPARSE_FFU_FORMAT_H + +#include + +#define FFU_SECURITY_SIGNATURE "SignedImage " + +#pragma pack(1) + +typedef struct _FFU_SECURITY_HEADER +{ + uint32_t cbSize; // size of struct, overall + uint8_t signature[12]; // "SignedImage " + uint32_t dwChunkSizeInKb; // size of a hashed chunk within the image + uint32_t dwAlgId; // algorithm used to hash + uint32_t dwCatalogSize; // size of catalog to validate + uint32_t dwHashTableSize; // size of hash table +} FFU_SECURITY_HEADER; + +#define FFU_SIGNATURE "ImageFlash " + +typedef struct _IMAGE_HEADER +{ + uint32_t cbSize; // sizeof(ImageHeader) + uint8_t Signature[12]; // "ImageFlash " + uint32_t ManifestLength; // in bytes + uint32_t dwChunkSize; // Used only during image generation. +} FFU_IMAGE_HEADER; + +typedef struct _STORE_HEADER +{ + uint32_t dwUpdateType; // indicates partial or full flash + uint16_t MajorVersion, MinorVersion; // used to validate struct + uint16_t FullFlashMajorVersion, FullFlashMinorVersion; // FFU version, i.e. the image format + uint8_t szPlatformId[192]; // string which indicates what device this FFU is intended to be written to + uint32_t dwBlockSizeInBytes; // size of an image block in bytes - the device's actual sector size may differ + uint32_t dwWriteDescriptorCount; // number of write descriptors to iterate through + uint32_t dwWriteDescriptorLength; // total size of all the write descriptors, in bytes (included so they can be read out up front and interpreted later) + uint32_t dwValidateDescriptorCount; // number of validation descriptors to check + uint32_t dwValidateDescriptorLength; // total size of all the validation descriptors, in bytes + uint32_t dwInitialTableIndex; // block index in the payload of the initial (invalid) GPT + uint32_t dwInitialTableCount; // count of blocks for the initial GPT, i.e. the GPT spans blockArray[idx..(idx + count -1)] + uint32_t dwFlashOnlyTableIndex; // first block index in the payload of the flash-only GPT (included so safe flashing can be accomplished) + uint32_t dwFlashOnlyTableCount; // count of blocks in the flash-only GPT + uint32_t dwFinalTableIndex; // index in the table of the real GPT + uint32_t dwFinalTableCount; // number of blocks in the real GPT + uint16_t NumOfStores; // Total number of stores (V2 only) + uint16_t StoreIndex; // Current store index, 1-based (V2 only) + uint64_t StorePayloadSize; // Payload data only, excludes padding (V2 only) + uint16_t DevicePathLength; // Length of the device path (V2 only) + uint16_t DevicePath[1]; // Device path has no NUL at then end (V2 only) +} FFU_STORE_HEADER; + +typedef struct _VALIDATION_ENTRY +{ + uint32_t dwSectorIndex; + uint32_t dwSectorOffset; + uint32_t dwByteCount; + uint8_t rgCompareData[1]; // size is dwByteCount +} FFU_VALIDATION_ENTRY; + +enum DISK_ACCESS_METHOD +{ + DISK_BEGIN = 0, + DISK_END = 2 +}; + +typedef struct _DISK_LOCATION +{ + uint32_t dwDiskAccessMethod; + uint32_t dwBlockIndex; +} FFU_DISK_LOCATION; + +typedef struct _BLOCK_DATA_ENTRY +{ + uint32_t dwLocationCount; + uint32_t dwBlockCount; + FFU_DISK_LOCATION rgDiskLocations[1]; +} FFU_BLOCK_DATA_ENTRY; +#pragma pack() + +#endif // LIBSPARSE_FFU_FORMAT_H diff --git a/libuuu/gen_ver.sh b/libuuu/gen_ver.sh new file mode 100755 index 0000000..fe9ed8a --- /dev/null +++ b/libuuu/gen_ver.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# Input parameters +file_to_write="$1" + +set -e + +if [ -f ../.tarball-version ] +then + echo "#define GIT_VERSION \"lib$(cat ../.tarball-version)\"" > "$file_to_write" + exit 0 +fi + +if [ "${APPVEYOR_BUILD_VERSION}" = "" ]; +then + echo build not in appveyor +else + git tag uuu_${APPVEYOR_BUILD_VERSION} +fi + +# Test if we are in a repo +if [ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" = "true" ]; +then + #echo "In a repo" + # Get the version of the last commit of the repo + version=`git describe --tags --long` + echo "#define GIT_VERSION \"lib$version\"" > $file_to_write +fi diff --git a/libuuu/hidreport.cpp b/libuuu/hidreport.cpp new file mode 100644 index 0000000..e27eadc --- /dev/null +++ b/libuuu/hidreport.cpp @@ -0,0 +1,98 @@ +/* + * Copyright 2020 NXP. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of the NXP Semiconductor nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "hidreport.h" +#include "libcomm.h" +#include "liberror.h" +#include "trans.h" + +#include + +HIDReport::~HIDReport() +{ +} + +void HIDReport::notify(size_t index, uuu_notify::NOTIFY_TYPE type) +{ + uuu_notify nf; + nf.type = type; + if(type == uuu_notify::NOTIFY_TRANS_POS) + nf.index = index + m_postion_base; + if (type == uuu_notify::NOTIFY_TRANS_SIZE) + { + nf.index = m_notify_total > index ? m_notify_total : index; + } + call_notify(nf); +} + +int HIDReport::read(std::vector &buff) +{ + if (buff.size() < m_size_in + m_size_payload) + { + set_last_err_string("buffer to small to get a package"); + return -1; + } + size_t rs; + int ret = m_pdev->read(buff.data(), m_size_in + m_size_payload, &rs); + + return ret; +} + +int HIDReport::write(const void *p, size_t sz, uint8_t report_id) +{ + notify(sz, uuu_notify::NOTIFY_TRANS_SIZE); + + const uint8_t * const buff = reinterpret_cast(p); + size_t off = 0; + for (; off < sz; off += m_size_out) + { + m_out_buff[0] = report_id; + + size_t s = sz - off; + if (s > m_size_out) + s = m_size_out; + + memcpy(m_out_buff.data() + m_size_payload, buff + off, s); + + int ret = m_pdev->write(m_out_buff.data(), report_id == 1? s + m_size_payload: m_size_out + m_size_payload); + + if (ret < 0) + return -1; + + if (off % 0x1F == 0) + { + notify(off, uuu_notify::NOTIFY_TRANS_POS); + } + } + + notify(sz, uuu_notify::NOTIFY_TRANS_POS); + return 0; +} diff --git a/libuuu/hidreport.h b/libuuu/hidreport.h new file mode 100644 index 0000000..495ec17 --- /dev/null +++ b/libuuu/hidreport.h @@ -0,0 +1,74 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#pragma once + +#include "libuuu.h" + +#include + +class TransBase; + +class HIDReport +{ +public: + HIDReport(TransBase *trans) : m_pdev{trans} + { + m_out_buff.resize(m_size_out + m_size_payload); + } + virtual ~HIDReport(); + + size_t get_out_package_size() noexcept { return m_size_out; } + virtual void notify(size_t index, uuu_notify::NOTIFY_TYPE type); + int read(std::vector &buff); + void set_notify_total(size_t notify_total) noexcept { m_notify_total = notify_total; } + void set_out_package_size(size_t sz) + { + m_size_out = sz; + m_out_buff.resize(m_size_out + m_size_payload); + } + void set_position_base(size_t position_base) noexcept { m_postion_base = position_base; } + void set_skip_notify(bool skip_notify) noexcept { m_skip_notify = skip_notify; } + int write(const void *p, size_t sz, uint8_t report_id); + int write(const std::vector &buff, uint8_t report_id) + { + return write(buff.data(), buff.size(), report_id); + } + +private: + size_t m_notify_total = 0; + std::vector m_out_buff; + TransBase * const m_pdev = nullptr; + size_t m_postion_base = 0; + size_t m_size_in = 64; + size_t m_size_out = 1024; + size_t m_size_payload = 1; + bool m_skip_notify = true; +}; diff --git a/libuuu/http.cpp b/libuuu/http.cpp new file mode 100644 index 0000000..e6b5f12 --- /dev/null +++ b/libuuu/http.cpp @@ -0,0 +1,470 @@ +/* + * Copyright 2019 NXP. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of the NXP Semiconductor nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#ifdef _WIN32 +// request += "Connection: Keep-Alive\n"; +#include +#include +#include +#pragma comment(lib, "Winhttp.lib") +#else +#include +#include +#include +#define INVALID_SOCKET -1 +#include +#endif + +#include "http.h" +#include "libuuu.h" +#include "liberror.h" +#include +#include +#include + +#ifdef UUUSSL +#include +#include + +class CUUUSSL +{ +public: + CUUUSSL() + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_library_init(); + SSLeay_add_ssl_algorithms(); + SSL_load_error_strings(); +#else + OPENSSL_init_ssl(0, nullptr); + SSLeay_add_ssl_algorithms(); +#endif + } + ~CUUUSSL() + { + } +}; + +static CUUUSSL g_uuussl; + +#endif + + +using namespace std; + +#ifdef _WIN32 +/* Win32 implement*/ + +HttpStream::HttpStream() +{ + m_buff.empty(); + m_hConnect = 0; + m_hSession = 0; + m_hRequest = 0; +} + +int HttpStream::HttpGetHeader(std::string host, std::string path, int port, bool ishttps) +{ + + m_hSession = WinHttpOpen(L"WinHTTP UUU/1.0", + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0); + + if (!m_hSession) + { + set_last_err_string("fail WinHttpOpen"); + return -1; + } + + wstring_convert> converter; + wstring whost = converter.from_bytes(host); + + if (m_hSession) + m_hConnect = WinHttpConnect(m_hSession, whost.c_str(), + port, 0); + + if (!m_hConnect) + { + set_last_err_string("Fail Connection"); + return -1; + } + + wstring wpath = converter.from_bytes(path); + + m_hRequest = WinHttpOpenRequest(m_hConnect, L"GET", wpath.c_str(), + nullptr, WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, + ishttps ?WINHTTP_FLAG_SECURE:0); + + BOOL bResults = FALSE; + if (!m_hRequest) + { + set_last_err_string("Fail WinHttpOpenRequest"); + return -1; + } + + bResults = WinHttpSendRequest(m_hRequest, + WINHTTP_NO_ADDITIONAL_HEADERS, 0, + WINHTTP_NO_REQUEST_DATA, 0, + 0, 0); + + if (!bResults) + { + set_last_err_string("Fail WinHttpSendRequest"); + return -1; + } + + bResults = WinHttpReceiveResponse(m_hRequest, nullptr); + + if (!bResults) + { + set_last_err_string("Fail WinHttpReceiveResponse"); + return -1; + } + + DWORD status = 0; + DWORD dwSize = sizeof(status); + WinHttpQueryHeaders(m_hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, &status, + &dwSize, WINHTTP_NO_HEADER_INDEX); + + if (status != HTTP_STATUS_OK) + { + set_last_err_string("HTTP status is not okay"); + return -1; + } + return 0; +} + +size_t HttpStream::HttpGetFileSize() +{ + DWORD dwSize = 0; + BOOL bResults = FALSE; + wstring out; + + WinHttpQueryHeaders(m_hRequest, WINHTTP_QUERY_CONTENT_LENGTH, + WINHTTP_HEADER_NAME_BY_INDEX, nullptr, + &dwSize, WINHTTP_NO_HEADER_INDEX); + + // Allocate memory for the buffer. + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + out.resize(dwSize / sizeof(WCHAR)); + + // Now, use WinHttpQueryHeaders to retrieve the header. + bResults = WinHttpQueryHeaders(m_hRequest, + WINHTTP_QUERY_CONTENT_LENGTH, + WINHTTP_HEADER_NAME_BY_INDEX, + (LPVOID)out.c_str(), &dwSize, + WINHTTP_NO_HEADER_INDEX); + } + return _wtoll(out.c_str()); +} + +int HttpStream::HttpDownload(char *buff, size_t sz) +{ + DWORD dwSize = 0; + DWORD dwDownloaded = 0; + while (sz) + { + if (!WinHttpQueryDataAvailable(m_hRequest, &dwSize)) + { + set_last_err_string("WinHttpQueryDataAvailable"); + return -1; + } + + if (dwSize > sz) + dwSize = sz; + + if (!WinHttpReadData(m_hRequest, (LPVOID)buff, + dwSize, &dwDownloaded)) + { + set_last_err_string("Fail at WinHttpReadData"); + return -1; + } + buff += dwDownloaded; + sz -= dwDownloaded; + } + return 0; +} + +HttpStream::~HttpStream() +{ + if (m_hRequest) + WinHttpCloseHandle(m_hRequest); + if (m_hConnect) + WinHttpCloseHandle(m_hConnect); + if (m_hSession) + WinHttpCloseHandle(m_hSession); +} + +#else + +HttpStream::HttpStream() +{ + m_buff.empty(); +} + +int HttpStream::SendPacket(char *buff, size_t sz) +{ +#ifdef UUUSSL + if(m_ssl) + return SSL_write((SSL*)m_ssl, buff, sz); +#endif + return send(m_socket, buff, sz, 0); +} + + +int HttpStream::RecvPacket(char *buff, size_t sz) +{ +#ifdef UUUSSL + if(m_ssl) + return SSL_read((SSL*)m_ssl, buff, sz); +#endif + return recv(m_socket, buff, sz, 0); +} + +int HttpStream::HttpGetHeader(std::string host, std::string path, int port, bool ishttps) +{ + int ret; + addrinfo *pAddrInfo; + char s_port[10]; + snprintf(s_port, 10, "%d", port); + + if (getaddrinfo(host.c_str(), s_port, 0, &pAddrInfo)) + { + set_last_err_string("get network address error"); + return -1; + } + + m_socket = socket(pAddrInfo->ai_family, pAddrInfo->ai_socktype, pAddrInfo->ai_protocol); + + struct timeval tv; + tv.tv_sec = 10; + tv.tv_usec = 0; + + setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); + + if (m_socket == INVALID_SOCKET) + { + set_last_err_string("Can't get sock"); + return -1; + } + + if (connect(m_socket, pAddrInfo->ai_addr, pAddrInfo->ai_addrlen)) + { + set_last_err_string("connect error"); + return -1; + } + + if(ishttps) + { +#ifdef UUUSSL + + const SSL_METHOD* meth = +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + TLSv1_2_client_method(); +#else + TLS_client_method(); +#endif + if(!meth) + { + set_last_err_string("Failure at TLSv1_2_client_method\n"); + return -1; + } + SSL_CTX *ctx = SSL_CTX_new (meth); + if(!ctx) + { + set_last_err_string("Error create ssl ctx\n"); + return -1; + } + m_ssl = SSL_new (ctx); + if(!m_ssl) + { + set_last_err_string("Error create SSL\n"); + return -1; + } + SSL_set_fd((SSL*)m_ssl, m_socket); + if( SSL_connect((SSL*)m_ssl) <= 0) + { + set_last_err_string("error build ssl connection"); + return -1; + } +#else + set_last_err_string("Can't support https"); + return -1; +#endif + } + + if(ishttps) + path = "https://" + host + path; + + string request = "GET " + path + " HTTP/1.1\r\n"; + request += "Host: " + host + "\r\n\r\n"; + + ret = SendPacket((char*)request.c_str(), request.size()); + if (ret != request.size()) + { + set_last_err_string("http send error"); + return -1; + } + + m_buff.resize(1024); + ret = RecvPacket((char*)m_buff.data(), m_buff.size()); + if (ret < 0) + { + set_last_err_string("http recv Error"); + return -1; + } + + int i; + for (i = 0; i < 1024 - 4; i++) + { + if (m_buff[i] == 0xd && + m_buff[i + 1] == 0xa && + m_buff[i + 2] == 0xd && + m_buff[i + 3] == 0xa) + { + break; + } + } + + if (i >= 1024 - 4) + { + set_last_err_string("Can't find termaniate"); + return -1; + } + + m_data_start = i + 4; + + string str; + str.resize(i + 2); + memcpy((void*)str.c_str(), m_buff.data(), i + 2); + + if (parser_response(str)) + return -1; + + return 0; +} + +size_t HttpStream::HttpGetFileSize() +{ + return atoll(m_response["Content-Length"].c_str()); +} + +int HttpStream::parser_response(string rep) +{ + size_t pos = rep.find("\r\n"); + if (pos == string::npos) + { + set_last_err_string("Can't find \r\n"); + return -1; + } + + string str = rep.substr(0, pos); + if (str != "HTTP/1.1 200 OK") + { + set_last_err_string(str); + return -1; + } + + m_response.clear(); + + while (pos != string::npos) + { + pos += 2; + size_t split = rep.find(':', pos); + if (split == string::npos) + break; + string key = rep.substr(pos, split - pos); + pos = rep.find("\r\n", pos); + string value = rep.substr(split + 1, pos - split - 1); + m_response[key] = value; + } + + return 0; +} + +int HttpStream::HttpDownload(char *buff, size_t sz) +{ + size_t left = 0; + if (m_data_start < m_buff.size()) + left = m_buff.size() - m_data_start; + + size_t trim_transfered = 0; + + if (left) + { + + trim_transfered = sz; + if (trim_transfered > left) + trim_transfered = left; + + memcpy(buff, m_buff.data() + m_data_start, trim_transfered); + m_data_start += trim_transfered; + } + + if (trim_transfered < sz) + { + int ret = 0; + sz -= trim_transfered; + buff += trim_transfered; + while (sz && ((ret = RecvPacket(buff, sz)) > 0)) + { + buff += ret; + sz -= ret; + } + + if (ret < 0) + { + set_last_err_string("recv error"); + return -1; + } + } + + return 0; +} + +HttpStream::~HttpStream() +{ + close(m_socket); +#ifdef UUUSSL + if(m_ssl) + { + SSL_CTX_free(SSL_get_SSL_CTX((SSL*)m_ssl)); + SSL_free((SSL*)m_ssl); + } +#endif +} + +#endif diff --git a/libuuu/http.h b/libuuu/http.h new file mode 100644 index 0000000..66321a7 --- /dev/null +++ b/libuuu/http.h @@ -0,0 +1,63 @@ +/* + * Copyright 2020 NXP. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of the NXP Semiconductor nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include +#include +#include + +class HttpStream +{ + std::vector m_buff; + int m_socket = -1; + std::map m_response; + size_t m_data_start; + +#ifdef _WIN32 + void far * m_hSession; + void far * m_hConnect; + void far * m_hRequest; +#endif + + void * m_ssl = nullptr; + int parser_response(std::string rep); +public: + HttpStream(); + int HttpGetHeader(std::string host, std::string path, int port = 80, bool ishttps=false); + size_t HttpGetFileSize(); + int HttpDownload(char *buff, size_t sz); + ~HttpStream(); + +private: + int RecvPacket(char *buff, size_t sz); + int SendPacket(char *buff, size_t sz); +}; diff --git a/libuuu/libcomm.h b/libuuu/libcomm.h new file mode 100644 index 0000000..93ab7e5 --- /dev/null +++ b/libuuu/libcomm.h @@ -0,0 +1,159 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#include +#include +#include +#include +#include +#pragma once + +using namespace std; + +void call_notify(struct uuu_notify nf); + +#define log printf +#define dbg printf + +int get_libusb_debug_level() noexcept; + +class string_ex : public std::string +{ +public: + + int format(const char *fmt, ...) + { + va_list args; + va_start(args, fmt); + size_t len = std::vsnprintf(nullptr, 0, fmt, args); + va_end(args); + + this->resize(len); + + va_start(args, fmt); + std::vsnprintf((char*)c_str(), len+1, fmt, args); + va_end(args); + + return 0; + } + void replace(char a, char b) + { + for (size_t i = 0; i < size(); i++) + if (at(i) == a) + (*this)[i] = b; + } +}; + +class Path : public string_ex +{ +public: + string get_file_name() + { + replace('\\', '/'); + size_t pos; + pos = rfind('/'); + if (pos == string::npos) + return *this; + return substr(pos + 1); + } +}; + +inline uint64_t EndianSwap(uint64_t x) { + return (((x & 0x00000000000000ffLL) << 56) | + ((x & 0x000000000000ff00LL) << 40) | + ((x & 0x0000000000ff0000LL) << 24) | + ((x & 0x00000000ff000000LL) << 8) | + ((x & 0x000000ff00000000LL) >> 8) | + ((x & 0x0000ff0000000000LL) >> 24) | + ((x & 0x00ff000000000000LL) >> 40) | + ((x & 0xff00000000000000LL) >> 56)); +} + +inline uint32_t EndianSwap(uint32_t x) +{ + return (x >> 24) | + ((x << 8) & 0x00FF0000) | + ((x >> 8) & 0x0000FF00) | + (x << 24); +} +inline uint16_t EndianSwap(uint16_t x) +{ + return (x >> 8) | + ((x << 8) & 0xFF00); +} + +inline string str_to_upper(const string &str) +{ + std::locale loc; + string s; + + for (size_t i = 0; i < str.size(); i++) + s.push_back(std::toupper(str[i], loc)); + + return s; +} + +inline string remove_quota(string str) +{ + if (!str.empty()) + { + if (str[0] == '"') + { + str.erase(0, 1); + if (!str.empty() && str[str.size() - 1] == '"') + str.erase(str.size() - 1, 1); + } + } + return str; +} + +inline bool compare_str(const string &str1, const string &str2, bool ignore_case) +{ + if (ignore_case) + return str_to_upper(str1) == str_to_upper(str2); + else + return str1 == str2; +} + +uint16_t str_to_uint16(const string &str, bool * conversion_suceeded = nullptr); +uint32_t str_to_uint32(const string &str, bool * conversion_suceeded = nullptr); +uint64_t str_to_uint64(const string &str, bool * conversion_suceeded = nullptr); + +template +inline T round_up(T x, T align) +{ + return (x + align - 1) / align * align; +} + +inline std::string trim(const std::string &s) +{ + auto wsfront = std::find_if_not(s.begin(), s.end(), [](int c) {return std::isspace(c); }); + return std::string(wsfront, std::find_if_not(s.rbegin(), std::string::const_reverse_iterator(wsfront), [](int c) {return std::isspace(c); }).base()); +} diff --git a/libuuu/liberror.h b/libuuu/liberror.h new file mode 100644 index 0000000..e2362be --- /dev/null +++ b/libuuu/liberror.h @@ -0,0 +1,39 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#pragma once + +#include + +void set_last_err_string(const std::string &str); +void set_last_err_id(int id); + +#define ERR_OUT_MEMORY -2 \ No newline at end of file diff --git a/libuuu/libuuu.h b/libuuu/libuuu.h new file mode 100644 index 0000000..71ebb50 --- /dev/null +++ b/libuuu/libuuu.h @@ -0,0 +1,142 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#ifndef __libuuu___ +#define __libuuu___ + +#include +#include + +#ifdef __cplusplus +#define EXT extern "C" +#else +#define EXT +#endif + +/** + * Get Last error string + * @return last error string +*/ +EXT const char * uuu_get_last_err_string(); + +/** +* Get Last error code +* @return last error code +*/ +EXT int uuu_get_last_err(); + +EXT const char * uuu_get_version_string(); + +/** + * 1.0.1 + * bit[31:24].bit[23:12].bit[11:0] + */ + +EXT int uuu_get_version(); + + + +struct uuu_notify +{ + enum NOTIFY_TYPE + { + NOTIFY_CMD_TOTAL, + NOTIFY_CMD_START, /* str is command name*/ + NOTIFY_CMD_END, /* status show command finish status. 0 is success. Other failure.*/ + NOTIFY_CMD_INDEX, /*Current running command index*/ + + NOTIFY_CMD_INFO, /* Status info string */ + + NOTIFY_PHASE_TOTAL, + NOTIFY_PHASE_INDEX, /*Current running phase*/ + + NOTIFY_TRANS_SIZE, /*Total size*/ + NOTIFY_TRANS_POS, /*Current finished transfer pos*/ + + NOTIFY_WAIT_FOR, + NOFITY_DEV_ATTACH, + + NOTIFY_DECOMPRESS_START, + NOTIFY_DECOMPRESS_SIZE, + NOTIFY_DECOMPRESS_POS, + + NOTIFY_DOWNLOAD_START, + NOTIFY_DOWNLOAD_END, + NOTIFY_THREAD_EXIT, + + NOTIFY_DONE, + }; + + NOTIFY_TYPE type; + uint64_t id; + uint64_t timestamp; + union + { + int status; + size_t index; + size_t total; + char *str; + }; +}; + +typedef int (*uuu_notify_fun)(struct uuu_notify, void *data); + +int uuu_register_notify_callback(uuu_notify_fun f, void *data); +int uuu_unregister_notify_callback(uuu_notify_fun f); + +typedef int(*uuu_show_cfg)(const char *pro, const char *chip, const char *comp, uint16_t vid, uint16_t pid, uint16_t bcdlow, uint16_t bcdhigh, void *p); +int uuu_for_each_cfg(uuu_show_cfg fn, void *p); + +typedef int(*uuu_ls_file)(const char *path, void *p); +int uuu_for_each_ls_file(uuu_ls_file fn, const char *path, void *p); + +typedef int(*uuu_ls_usb_devices)(const char *path, const char *chip, const char *pro, uint16_t vid, uint16_t pid, uint16_t bcd, void *p); +int uuu_for_each_devices(uuu_ls_usb_devices fn, void *p); + +int uuu_run_cmd(const char * cmd, int dry); +int uuu_run_cmd_script(const char *script, int dry); + +int uuu_auto_detect_file(const char * filename); +int uuu_wait_uuu_finish(int deamon, int dry); +int uuu_add_usbpath_filter(const char *path); + +/*Set timeout wait for known devices appeared*/ +int uuu_set_wait_timeout(int timeout_in_seconds); +/*Set timeout wait for next devices appeared, e.g. FB -> FBK*/ +int uuu_set_wait_next_timeout(int timeout_in_seconds); +/*Set usb device polling period */ +void uuu_set_poll_period(int period_in_milliseconds); +/* + * bit 0:15 for libusb + * bit 16:31 for uuu + */ +void uuu_set_debug_level(uint32_t mask); + +#endif diff --git a/libuuu/notify.cpp b/libuuu/notify.cpp new file mode 100644 index 0000000..4739e4e --- /dev/null +++ b/libuuu/notify.cpp @@ -0,0 +1,79 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include "libuuu.h" + +#include +#include +#include +#include +#include + +using namespace std; + +static map g_notification_map; +static mutex g_mutex_notify; + +using namespace std::chrono; +static const time_point g_now = steady_clock::now(); + +int uuu_register_notify_callback(uuu_notify_fun f, void *data) +{ + std::lock_guard lock(g_mutex_notify); + + return g_notification_map.emplace(f, data).second ? 0 : 1; +} + +int uuu_unregister_notify_callback(uuu_notify_fun f) +{ + std::lock_guard lock(g_mutex_notify); + + return g_notification_map.erase(f) > 0 ? 0 : 1; +} + +void call_notify(struct uuu_notify nf) +{ + //Change RW lock later; + std::lock_guard lock(g_mutex_notify); + + nf.id = std::hash{}(std::this_thread::get_id()); + nf.timestamp = static_cast( + duration_cast(steady_clock::now() - g_now).count()); + + for (const auto &item : g_notification_map) + { + try { + item.first(nf, item.second); + } catch (const std::exception& e) { + std::cerr << "notify exception: " << e.what() << std::endl; + } + } +} diff --git a/libuuu/rominfo.cpp b/libuuu/rominfo.cpp new file mode 100644 index 0000000..dc1b147 --- /dev/null +++ b/libuuu/rominfo.cpp @@ -0,0 +1,249 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include "rominfo.h" +#include "buffer.h" +#include "config.h" +#include "libcomm.h" +#include + +#include + +using namespace std; + +static constexpr std::array g_RomInfo +{ + ROM_INFO{ "MX6Q", 0x00910000, ROM_INFO_HID | ROM_INFO_HID_MX6 }, + ROM_INFO{ "MX6D", 0x00910000, ROM_INFO_HID | ROM_INFO_HID_MX6 }, + ROM_INFO{ "MX6SL", 0x00910000, ROM_INFO_HID | ROM_INFO_HID_MX6 }, + ROM_INFO{ "MX7D", 0x00911000, ROM_INFO_HID | ROM_INFO_HID_MX6 | ROM_INFO_HID_SKIP_DCD }, + ROM_INFO{ "MX6UL", 0x00910000, ROM_INFO_HID | ROM_INFO_HID_MX6 | ROM_INFO_HID_SKIP_DCD }, + ROM_INFO{ "MX6ULL", 0x00910000, ROM_INFO_HID | ROM_INFO_HID_MX6 | ROM_INFO_HID_SKIP_DCD }, + ROM_INFO{ "MX6SLL", 0x00910000, ROM_INFO_HID | ROM_INFO_HID_MX6 | ROM_INFO_HID_SKIP_DCD }, + ROM_INFO{ "MX8MQ", 0x00910000, ROM_INFO_HID | ROM_INFO_HID_MX6 | ROM_INFO_HID_SKIP_DCD }, + ROM_INFO{ "MX7ULP", 0x2f018000, ROM_INFO_HID | ROM_INFO_HID_MX6 | ROM_INFO_HID_SKIP_DCD }, + ROM_INFO{ "MXRT106X", 0x1000, ROM_INFO_HID | ROM_INFO_HID_MX6 | ROM_INFO_HID_SKIP_DCD }, + ROM_INFO{ "MX8QXP", 0x0, ROM_INFO_HID | ROM_INFO_HID_NO_CMD | ROM_INFO_HID_UID_STRING }, + ROM_INFO{ "MX28", 0x0, ROM_INFO_HID}, + ROM_INFO{ "MX815", 0x0, ROM_INFO_HID | ROM_INFO_HID_NO_CMD | ROM_INFO_HID_UID_STRING | ROM_INFO_HID_EP1 | ROM_INFO_HID_PACK_SIZE_1020 | ROM_INFO_HID_ROMAPI}, + ROM_INFO{ "SPL", 0x0, ROM_INFO_HID | ROM_INFO_HID_MX6 | ROM_INFO_SPL_JUMP | ROM_INFO_HID_SDP_NO_MAX_PER_TRANS}, + ROM_INFO{ "SPL1", 0x0, ROM_INFO_HID | ROM_INFO_HID_MX6 | ROM_INFO_SPL_JUMP | ROM_INFO_HID_SDP_NO_MAX_PER_TRANS | ROM_INFO_AUTO_SCAN_UBOOT_POS}, +}; + +const ROM_INFO * search_rom_info(const std::string &s) +{ + for (const auto &rom_info : g_RomInfo) { + if (s == rom_info.m_name) + { + return &rom_info; + } + } + + return nullptr; +} + +const ROM_INFO * search_rom_info(const ConfigItem *item) +{ + if (item == nullptr) + { + return nullptr; + } + + const ROM_INFO * const p = search_rom_info(item->m_chip); + if (p) + { + return p; + } + + return search_rom_info(item->m_compatible); +} + + +#define IV_MAX_LEN 32 +#define HASH_MAX_LEN 64 + +#define CONTAINER_HDR_ALIGNMENT 0x400 +static constexpr uint8_t CONTAINER_TAG = 0x87; + +#pragma pack (1) +struct rom_container { + uint8_t version; + uint8_t length_l; + uint8_t length_m; + uint8_t tag; + uint32_t flags; + uint16_t sw_version; + uint8_t fuse_version; + uint8_t num_images; + uint16_t sig_blk_offset; + uint16_t reserved; +}; + +struct rom_bootimg { + uint32_t offset; + uint32_t size; + uint64_t destination; + uint64_t entry; + uint32_t flags; + uint32_t meta; + uint8_t hash[HASH_MAX_LEN]; + uint8_t iv[IV_MAX_LEN]; +}; + + +static constexpr uint32_t IMG_V2X = 0x0B; + +#pragma pack () + + +size_t GetContainerActualSize(shared_ptr p, size_t offset, bool bROMAPI) +{ + if(bROMAPI) + return p->size() - offset; + + auto hdr = reinterpret_cast(p->data() + offset + CONTAINER_HDR_ALIGNMENT); + if (hdr->tag != CONTAINER_TAG) + { + return p->size() - offset; + } + + /* Check if include V2X container*/ + auto image = reinterpret_cast(p->data() + offset + CONTAINER_HDR_ALIGNMENT + + sizeof(struct rom_container)); + + unsigned int cindex = 1; + if ((image->flags & 0xF) == IMG_V2X) + { + cindex = 2; + hdr = reinterpret_cast(p->data() + offset + cindex * CONTAINER_HDR_ALIGNMENT); + if (hdr->tag != CONTAINER_TAG) + { + return p->size() - offset; + } + } + + image = reinterpret_cast(p->data() + offset + cindex * CONTAINER_HDR_ALIGNMENT + + sizeof(struct rom_container) + + sizeof(struct rom_bootimg) * (hdr->num_images - 1)); + + uint32_t sz = image->size + image->offset + cindex * CONTAINER_HDR_ALIGNMENT; + + sz = round_up(sz, static_cast(CONTAINER_HDR_ALIGNMENT)); + + if (sz > (p->size() - offset)) + { + return p->size() - offset; + } + + return sz; +} + +bool CheckHeader(uint32_t *p) +{ + static constexpr std::array FlashHeaderMagic + { + 0xc0ffee01, + 0x42464346 + }; + + for (const auto magic_val : FlashHeaderMagic) + { + if (*p == magic_val) + { + return true; + } + } + + return false; +} + +size_t GetFlashHeaderSize(shared_ptr p, size_t offset) +{ + static constexpr std::array offsets + { + 0, + 0x400, + 0x1fc, + 0x5fc + }; + + for (const auto test_offset : offsets) { + if (p->m_avaible_size < (offset + test_offset)) { + return 0; + } + + if (CheckHeader(reinterpret_cast(p->data() + offset + test_offset))) { + return 0x1000; + } + } + + return 0; +} + +bool IsMBR(shared_ptr p) +{ + uint16_t * m = (uint16_t *)(p->data() + 510); + if (*m == 0xaa55) + return true; + return false; +} + +size_t ScanTerm(std::shared_ptr p, size_t &pos, size_t offset, size_t limited) +{ + const char *tag = "UUUBURNXXOEUZX7+A-XY5601QQWWZ"; + + if (limited >= p->m_avaible_size) + limited = p->m_avaible_size; + + limited = limited - strlen(tag) - 64; + + if (offset > limited) + return 0; + + for (size_t i = offset; i < limited; i++) + { + char *c = (char*)p->data() + i; + size_t length = strlen(tag); + size_t j; + for (j = 0; j < length; j++) + { + if (tag[j] != c[j]) + break; + } + if (j == length) + { + pos = i; + return atoll(c + length); + } + } + + return 0; +} \ No newline at end of file diff --git a/libuuu/rominfo.h b/libuuu/rominfo.h new file mode 100644 index 0000000..5d27e7c --- /dev/null +++ b/libuuu/rominfo.h @@ -0,0 +1,72 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#pragma once + +#include +#include +#include + +class ConfigItem; +class FileBuffer; + +constexpr uint32_t ROM_INFO_HID = 0x1; +constexpr uint32_t ROM_INFO_HID_MX23 = 0x2; +constexpr uint32_t ROM_INFO_HID_MX50 = 0x4; +constexpr uint32_t ROM_INFO_HID_MX6 = 0x8; +constexpr uint32_t ROM_INFO_HID_SKIP_DCD = 0x10; +constexpr uint32_t ROM_INFO_HID_MX8_MULTI_IMAGE = 0x20; +constexpr uint32_t ROM_INFO_HID_MX8_STREAM = 0x40; +constexpr uint32_t ROM_INFO_HID_UID_STRING = 0x80; +// Omitted value: 0x100 +// Omitted value: 0x200 +constexpr uint32_t ROM_INFO_HID_NO_CMD = 0x400; +constexpr uint32_t ROM_INFO_SPL_JUMP = 0x800; +constexpr uint32_t ROM_INFO_HID_EP1 = 0x1000; +constexpr uint32_t ROM_INFO_HID_PACK_SIZE_1020 = 0x2000; +constexpr uint32_t ROM_INFO_HID_SDP_NO_MAX_PER_TRANS = 0x4000; +constexpr uint32_t ROM_INFO_AUTO_SCAN_UBOOT_POS = 0x8000; +constexpr uint32_t ROM_INFO_HID_ROMAPI = 0x10000; + +struct ROM_INFO +{ + const char * m_name; + uint32_t free_addr; + uint32_t flags; +}; + +const ROM_INFO * search_rom_info(const std::string &s); +const ROM_INFO * search_rom_info(const ConfigItem *item); + +size_t GetContainerActualSize(std::shared_ptr p, size_t offset, bool bROMAPI=false); +size_t GetFlashHeaderSize(std::shared_ptr p, size_t offset = 0); +bool IsMBR(std::shared_ptr p); +size_t ScanTerm(std::shared_ptr p, size_t &pos, size_t offset=512, size_t limited=0x800000); diff --git a/libuuu/sdp.cpp b/libuuu/sdp.cpp new file mode 100644 index 0000000..8018595 --- /dev/null +++ b/libuuu/sdp.cpp @@ -0,0 +1,773 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include +#include "sdps.h" +#include "hidreport.h" +#include "liberror.h" +#include "libcomm.h" +#include "buffer.h" +#include "sdp.h" +#include "rominfo.h" +#include "libusb.h" +#include "trans.h" + +#include + +int SDPCmdBase::check_ack(HIDReport *report, uint32_t ack) +{ + if (get_hab_type(report) == HabUnknown) + return -1; + + uint32_t status; + if (get_status(report, status, 4)) + return -1; + + if (ack != status) + { + set_last_err_string("Status Miss matched"); + return -1; + } + return 0; +} + +SDPCmdBase::HAB_t SDPCmdBase::get_hab_type(HIDReport *report) +{ + uint32_t status; + if (get_status(report, status, 3)) + return HabUnknown; + + if (status == HabEnabled) + return HabEnabled; + + if (status == HabDisabled) + return HabDisabled; + + set_last_err_string("unknown hab type"); + return HabUnknown; +} + +int SDPCmdBase::get_status(HIDReport *p, uint32_t &status, uint8_t report_id) +{ + m_input.resize(1025); + m_input[0] = report_id; + int ret = p->read(m_input); + if (ret < 0) + return -1; + + if (m_input.size() < (1 + sizeof(uint32_t))) + { + set_last_err_string("HID report size is too small"); + return -1; + } + + status = *(uint32_t*)(m_input.data() + 1); + return 0; +} + +int SDPCmdBase::init_cmd() +{ + memset(&m_spdcmd, 0, sizeof(m_spdcmd)); + insert_param_info("-scanlimited", &m_scan_limited, Param::Type::e_uint64); + return 0; +} + +IvtHeader *SDPCmdBase::search_ivt_header(shared_ptr data, size_t &off, size_t limit) +{ + if (limit >= data->m_avaible_size) + limit = data->m_avaible_size; + + for (; off < limit; off += 0x4) + { + IvtHeader *p = (IvtHeader*)(data->data() + off); + if (p->IvtBarker == IVT_BARKER_HEADER) + return p; + if (p->IvtBarker == IVT_BARKER2_HEADER) + { + BootData *pDB = (BootData *) &(data->at(off + p->BootData - p->SelfAddr)); + + /*Skip HDMI firmware for i.MX8MQ*/ + if (pDB->PluginFlag & 0xFFFFFFFE) + continue; + return p; + } + } + off = -1; + return nullptr; +} + +int SDPCmdBase::send_cmd(HIDReport *p) +{ + return p->write(&m_spdcmd, sizeof(m_spdcmd), 1); +} + +SDPDcdCmd::SDPDcdCmd(char *p) : SDPCmdBase(p) +{ + insert_param_info("dcd", nullptr, Param::Type::e_null); + insert_param_info("-f", &m_filename, Param::Type::e_string_filename); + insert_param_info("-dcdaddr", &m_dcd_addr, Param::Type::e_uint32); + m_dcd_addr = 0; +} + +int SDPDcdCmd::run(CmdCtx*ctx) +{ + const ROM_INFO * rom = search_rom_info(ctx->m_config_item); + if (rom == nullptr) + { + string_ex err; + err.format("%s:%d can't get rom info", __FUNCTION__, __LINE__); + set_last_err_string(err); + return -1; + } + init_cmd(); + + shared_ptr buff, p = get_file_buffer(m_filename, true); + + if (!p) + return -1; + + buff = p->request_data(0, m_scan_limited); + + size_t off = 0; + IvtHeader *pIVT = search_ivt_header(buff, off); + if (pIVT == nullptr) + { + return 0; + } + + if (pIVT->DCDAddress == 0) + return 0; + + uint8_t * pdcd = &(buff->at(off + pIVT->DCDAddress - pIVT->SelfAddr)); + + if (pdcd[0] != HAB_TAG_DCD) + { + string_ex err; + err.format("%s:%d DCD TAG miss matched", __FUNCTION__, __LINE__); + set_last_err_string(err); + return -1; + } + + uint32_t size = (pdcd[1] << 8) | pdcd[2]; + + if (size >= m_scan_limited) + { + set_last_err_string("dcd bigger than 8M"); + return -1; + } + + // point maybe change after new requestion buffer. + pdcd = &(buff->at(off + pIVT->DCDAddress - pIVT->SelfAddr)); + + m_spdcmd.m_cmd = ROM_KERNEL_CMD_DCD_WRITE; + m_spdcmd.m_addr = EndianSwap(m_dcd_addr ? m_dcd_addr : rom->free_addr); + m_spdcmd.m_count = EndianSwap(size); + + HIDTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + HIDReport report(&dev); + if (report.write(&m_spdcmd, sizeof(m_spdcmd), 1)) + return -1; + + if (report.write(pdcd, size, 2)) + return -1; + + if (check_ack(&report, ROM_WRITE_ACK)) + return -1; + + return 0; +} + +SDPSkipDCDCmd::SDPSkipDCDCmd(char *p) : SDPCmdBase(p) +{ + m_spdcmd.m_cmd = ROM_KERNEL_CMD_SKIP_DCD_HEADER; +} + +int SDPSkipDCDCmd::run(CmdCtx*ctx) +{ + HIDTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + HIDReport report(&dev); + if (report.write(&m_spdcmd, sizeof(m_spdcmd), 1)) + return -1; + + if (check_ack(&report, ROM_OK_ACK)) + return -1; + + return 0; +} + +SDPBootCmd::SDPBootCmd(char *p) : SDPCmdBase(p) +{ + insert_param_info("boot", nullptr, Param::Type::e_null); + insert_param_info("-f", &m_filename, Param::Type::e_string_filename); + insert_param_info("-nojump", &m_nojump, Param::Type::e_bool); + insert_param_info("-cleardcd", &m_clear_dcd, Param::Type::e_bool); + insert_param_info("-dcdaddr", &m_dcd_addr, Param::Type::e_uint32); + insert_param_info("-scanlimited", &m_scan_limited, Param::Type::e_uint64); +} + +int SDPBootCmd::run(CmdCtx *ctx) +{ + string str; + str = "SDP: dcd -f "; + str += m_filename; + if (m_dcd_addr) { + str += " -dcdaddr "; + str += std::to_string(m_dcd_addr); + } + + if (m_scan_limited != UINT64_MAX) + { + str += " -scanlimited "; + str += std::to_string(m_scan_limited); + } + + SDPDcdCmd dcd((char *)str.c_str()); + + if (m_scan_limited != UINT64_MAX) + { + str += " -scanlimited "; + str += std::to_string(m_scan_limited); + } + + if (dcd.parser()) return -1; + if (dcd.run(ctx)) return -1; + + str = "SDP: write -f "; + str += m_filename; + str += " -ivt 0"; + + if (m_scan_limited != UINT64_MAX) + { + str += " -scanlimited "; + str += std::to_string(m_scan_limited); + } + + SDPWriteCmd wr((char *)str.c_str()); + if (wr.parser()) return -1; + if (wr.run(ctx)) return -1; + + str = "SDP: jump -f "; + str += m_filename; + str += " -ivt 0"; + if (m_clear_dcd) + str += " -cleardcd"; + + if (m_scan_limited != UINT64_MAX) + { + str += " -scanlimited "; + str += std::to_string(m_scan_limited); + } + + SDPJumpCmd jmp((char *)str.c_str()); + if (!m_nojump) + { + if (jmp.parser()) return -1; + if (jmp.run(ctx)) return -1; + } + + SDPBootlogCmd log(nullptr); + log.run(ctx); + + return 0; +} + +SDPStatusCmd::SDPStatusCmd(char *p) : SDPCmdBase(p) +{ + m_spdcmd.m_cmd = ROM_KERNEL_CMD_ERROR_STATUS; + insert_param_info("status", nullptr, Param::Type::e_null); +} + +int SDPStatusCmd::run(CmdCtx *ctx) +{ + HIDTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + HIDReport report(&dev); + if (report.write(&m_spdcmd, sizeof(m_spdcmd), 1)) + return -1; + + if (get_hab_type(&report) == HabUnknown) + return -1; + + uint32_t status; + if (get_status(&report, status, 4)) + return -1; + + return 0; +} + +SDPWriteCmd::SDPWriteCmd(char *p) : SDPCmdBase(p) +{ + m_spdcmd.m_cmd = ROM_KERNEL_CMD_WR_FILE; + m_PlugIn = -1; + m_Ivt = -1; + m_max_download_pre_cmd = 0x200000; + m_offset = 0; + m_bIvtReserve = false; + m_download_addr = 0; + m_bskipspl = false; + m_bscanterm = false; + + insert_param_info("write", nullptr, Param::Type::e_null); + insert_param_info("-f", &m_filename, Param::Type::e_string_filename); + insert_param_info("-ivt", &m_Ivt, Param::Type::e_uint32); + insert_param_info("-addr", &m_download_addr, Param::Type::e_uint32); + insert_param_info("-offset", &m_offset, Param::Type::e_uint32); + insert_param_info("-skipspl", &m_bskipspl, Param::Type::e_bool); + insert_param_info("-skipfhdr", &m_bskipfhdr, Param::Type::e_bool); + insert_param_info("-scanterm", &m_bscanterm, Param::Type::e_bool); +} + +int SDPWriteCmd::run(CmdCtx*ctx) +{ + size_t size; + uint8_t *pbuff; + int offset = 0; + + shared_ptr fbuff, p1= get_file_buffer(m_filename, true); + + if (p1 == nullptr) + return -1; + + fbuff = p1->request_data(0, m_scan_limited); + + if (m_Ivt < 0) + { + pbuff = fbuff->data(); + size = fbuff->size(); + + offset = m_offset; + + if (m_bskipfhdr) + offset += GetFlashHeaderSize(fbuff, offset); + + size_t pos = 0, length; + if (m_bscanterm) + { + if (IsMBR(fbuff)) + { + length = ScanTerm(fbuff, pos); + if (length == 0) + { + set_last_err_string("This wic have NOT terminate tag after bootloader, please use new yocto"); + return -1; + } + + offset = pos - length; + if (offset < 0) + { + set_last_err_string("This wic boot length is wrong"); + return -1; + } + size = pos; + } + } + + if (m_bskipspl) { + const ROM_INFO * rom = search_rom_info(ctx->m_config_item); + if(! (rom->flags & ROM_INFO_AUTO_SCAN_UBOOT_POS)) + { + set_last_err_string("SPL doesn't support auto scan uboot position"); + return -1; + } + + size_t off = offset; + IvtHeader *pIvt = search_ivt_header(fbuff, off, 0x100000); + if (pIvt) + { + BootData *pDB = (BootData *) &(fbuff->at(off + pIvt->BootData - pIvt->SelfAddr)); + offset = off + pDB->ImageSize - (pIvt->SelfAddr - pDB->ImageStartAddr); + } + else + { + offset += GetContainerActualSize(fbuff, offset); + } + + if (offset >= fbuff->m_avaible_size) + { + set_last_err_string("Unknown Image type, can't use skipspl format"); + return -1; + } + } + + size -= offset; + } + else + { + size_t off = 0; + IvtHeader *pIvt = search_ivt_header(fbuff, off); + for (int i = 0; i < m_Ivt; i++) + { + off += sizeof(IvtHeader); + pIvt = search_ivt_header(fbuff, off, m_scan_limited); + } + if (pIvt == nullptr) + { + set_last_err_string("Cannot find valid IVT header"); + return -1; + } + + BootData *pDB = (BootData *) &(fbuff->at(off + pIvt->BootData - pIvt->SelfAddr)); + + m_download_addr = pIvt->SelfAddr; + //size = fbuff->size() - off; + size = pDB->ImageSize - (pIvt->SelfAddr - pDB->ImageStartAddr); + + if (size >= m_scan_limited) + { + set_last_err_string("TODO: image is too big"); + return -1; + } + + //ImageSize may be bigger than Imagesize because ImageSize include IVT offset + //Difference boot storage have difference IVT offset. + if (size > fbuff->size() - off) + size = fbuff->size() - off; + + pbuff = (uint8_t*)pIvt; + } + return run(ctx, pbuff + offset, size, m_download_addr); +} + +int SDPWriteCmd::run(CmdCtx *ctx, void *pbuff, size_t size, uint32_t addr) +{ + HIDTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + HIDReport report(&dev); + + report.set_notify_total(size); + + const ROM_INFO * rom = search_rom_info(ctx->m_config_item); + + size_t max = m_max_download_pre_cmd; + + /* SPL needn't split transfer */ + if (rom && (rom ->flags & ROM_INFO_HID_SDP_NO_MAX_PER_TRANS)) + max = size; + + for (size_t i=0; i < size; i += max) + { + size_t sz; + sz = size - i; + if (sz > max) + sz = max; + + m_spdcmd.m_addr = EndianSwap((uint32_t)(addr + i)); // force use 32bit endian swap function; + m_spdcmd.m_count = EndianSwap((uint32_t)sz); //force use 32bit endian swap function; + + report.set_position_base(i); + report.set_skip_notify(true); + + if (report.write(&m_spdcmd, sizeof(m_spdcmd), 1)) + return -1; + + report.set_skip_notify(false); + + if (report.write(((uint8_t*)pbuff)+i, sz, 2)) + return -1; + + if (check_ack(&report, ROM_STATUS_ACK)) + return -1; + } + + return 0; +} + +SDPReadMemCmd::SDPReadMemCmd(char *p) : SDPCmdBase(p) +{ + m_spdcmd.m_cmd = ROM_KERNEL_CMD_RD_MEM; + + insert_param_info("rdmem", nullptr, Param::Type::e_null); + insert_param_info("-addr", &m_mem_addr, Param::Type::e_uint32); + insert_param_info("-format", &m_mem_format, Param::Type::e_uint32); +} + +int SDPReadMemCmd::run(CmdCtx *ctx) +{ + HIDTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + HIDReport report(&dev); + + printf("\nReading address 0x%08X ...\n", m_mem_addr); + m_spdcmd.m_addr = EndianSwap(m_mem_addr); + m_spdcmd.m_format = m_mem_format; + switch (m_mem_format) { + case 0x8: + m_spdcmd.m_count = EndianSwap((uint32_t)0x1); + break; + case 0x10: + m_spdcmd.m_count = EndianSwap((uint32_t)0x2); + break; + case 0x20: + m_spdcmd.m_count = EndianSwap((uint32_t)0x4); + break; + default: + set_last_err_string("Invalid format, use <8|16|32>"); + return -1; + break; + } + + if (report.write(&m_spdcmd, sizeof(m_spdcmd), 1)) + return -1; + + if (get_hab_type(&report) == HabUnknown) + return -1; + + uint32_t mem_value; + if (get_status(&report, mem_value, 4) == 0) + { + printf("\nValue of address 0x%08X: ", m_mem_addr); + switch (m_mem_format) { + case 0x8: + printf("0x%02X\n", mem_value & 0xff); + break; + case 0x10: + printf("0x%04X\n", mem_value & 0xffff); + break; + case 0x20: + printf("0x%08X\n", mem_value); + break; + default: + set_last_err_string("Invalid format, use <8|16|32>"); + return -1; + } + } + + return 0; +} + +SDPWriteMemCmd::SDPWriteMemCmd(char *p) : SDPCmdBase(p) +{ + m_spdcmd.m_cmd = ROM_KERNEL_CMD_WR_MEM; + + insert_param_info("wrmem", nullptr, Param::Type::e_null); + insert_param_info("-addr", &m_mem_addr, Param::Type::e_uint32); + insert_param_info("-format", &m_mem_format, Param::Type::e_uint32); + insert_param_info("-value", &m_mem_value, Param::Type::e_uint32); +} + +int SDPWriteMemCmd::run(CmdCtx *ctx) +{ + HIDTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + HIDReport report(&dev); + + printf("\nWriting 0x%08X to address 0x%08X ...\n", m_mem_value, m_mem_addr); + m_spdcmd.m_addr = EndianSwap(m_mem_addr); + m_spdcmd.m_format = m_mem_format; + switch (m_mem_format) { + case 0x8: + m_spdcmd.m_count = EndianSwap((uint32_t)0x1); + break; + case 0x10: + m_spdcmd.m_count = EndianSwap((uint32_t)0x2); + break; + case 0x20: + m_spdcmd.m_count = EndianSwap((uint32_t)0x4); + break; + default: + set_last_err_string("Invalid format, use <8|16|32>"); + return -1; + break; + } + m_spdcmd.m_data = EndianSwap(m_mem_value); + + if (report.write(&m_spdcmd, sizeof(m_spdcmd), 1)) + return -1; + + if (get_hab_type(&report) == HabUnknown) + return -1; + + uint32_t status; + + if (get_status(&report, status, 4) < 0 || status != ROM_WRITE_ACK) { + + string_ex err; + err.format("%s:%d Failed to write to address 0x%X", + __FUNCTION__, __LINE__, m_mem_addr); + set_last_err_string(err); + } + + return 0; +} + +SDPJumpCmd::SDPJumpCmd(char *p) : SDPCmdBase(p) +{ + m_spdcmd.m_cmd = ROM_KERNEL_CMD_JUMP_ADDR; + insert_param_info("jump", nullptr, Param::Type::e_null); + insert_param_info("-f", &m_filename, Param::Type::e_string_filename); + insert_param_info("-ivt", &m_Ivt, Param::Type::e_uint32); + insert_param_info("-plugin", &m_PlugIn, Param::Type::e_bool); + insert_param_info("-addr", &m_jump_addr, Param::Type::e_uint32); + insert_param_info("-cleardcd", &m_clear_dcd, Param::Type::e_bool); +} + +int SDPJumpCmd::run(CmdCtx *ctx) +{ + const ROM_INFO * rom = search_rom_info(ctx->m_config_item); + + HIDTrans dev; + if (dev.open(ctx->m_dev)) + return -1; + + HIDReport report(&dev); + + if (rom == nullptr) + { + string_ex err; + err.format("%s:%d can't get rom info", __FUNCTION__, __LINE__); + set_last_err_string(err); + return -1; + } + + if (rom->flags & ROM_INFO_SPL_JUMP) + { + m_spdcmd.m_addr = EndianSwap(m_jump_addr); + if (report.write(&m_spdcmd, sizeof(m_spdcmd), 1)) + return -1; + + //Omit last return value. + check_ack(&report, ROM_OK_ACK); + return 0; + } + + shared_ptr buff, p1 = get_file_buffer(m_filename, true); + if (!p1) + return -1; + + buff = p1->request_data(0, m_scan_limited); + + size_t off = 0; + IvtHeader *pIVT = search_ivt_header(buff, off, m_scan_limited); + + for (int i = 0; i < m_Ivt; i++) + { + off += sizeof(IvtHeader); + pIVT = search_ivt_header(buff, off); + } + + if (pIVT == nullptr) + { + set_last_err_string("Cannot find valid IVT header"); + return -1; + } + + m_spdcmd.m_addr = EndianSwap(pIVT->SelfAddr); + + + if (rom->flags & ROM_INFO_HID_SKIP_DCD && !m_clear_dcd) + { + SDPSkipDCDCmd skipcmd(nullptr); + if (skipcmd.run(ctx)) + return -1; + } + else + { /*Clear DCD*/ + vector ivt; + /* Need send out whole report size buffer avoid overwrite other data + * Some platform require receive whole package for report id = 2 + */ + ivt.resize(report.get_out_package_size()); + + size_t sz = buff->size(); + sz -= (uint8_t*)pIVT - (uint8_t*)buff->data(); + + if (sz > ivt.size()) + sz = ivt.size(); + + memcpy(ivt.data(), pIVT, sz); + + IvtHeader *header = (IvtHeader *)ivt.data(); + header->DCDAddress = 0; + + SDPWriteCmd writecmd(nullptr); + if(writecmd.run(ctx, header, ivt.size(), pIVT->SelfAddr)) + return -1; + } + + if (report.write(&m_spdcmd, sizeof(m_spdcmd), 1)) + return -1; + + //Omit last return value. + check_ack(&report, ROM_OK_ACK); + + return 0; +} + +SDPBootlogCmd::SDPBootlogCmd(char *p) : SDPCmdBase(p) +{ + insert_param_info("blog", nullptr, Param::Type::e_null); +} + +int SDPBootlogCmd::run(CmdCtx *ctx) +{ + HIDTrans dev{2000}; + + if (dev.open(ctx->m_dev)) + return -1; + + HIDReport report(&dev); + + vector v(65); + v[0] = 'I'; + + uuu_notify nt; + nt.type = uuu_notify::NOTIFY_CMD_INFO; + + int ret; + while (1) + { + ret = report.read(v); + if (ret) + return 0; + else + { + nt.str = (char*)(v.data() + 4); + v[5] = 0; + call_notify(nt); + continue; + } + } + return 0; +} diff --git a/libuuu/sdp.h b/libuuu/sdp.h new file mode 100644 index 0000000..0329bd9 --- /dev/null +++ b/libuuu/sdp.h @@ -0,0 +1,218 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#pragma once + +#include "cmd.h" + +#include + +class FileBuffer; +class HIDReport; + +#pragma pack (1) +struct SDPCmd { + uint16_t m_cmd; + uint32_t m_addr; + uint8_t m_format; + uint32_t m_count; + uint32_t m_data; + uint8_t m_rsvd; +}; + +struct IvtHeader +{ + uint32_t IvtBarker; + uint32_t ImageStartAddr; + uint32_t Reserved; + uint32_t DCDAddress; + uint32_t BootData; + uint32_t SelfAddr; + uint32_t Reserved2[2]; +}; + +struct BootData +{ + uint32_t ImageStartAddr; + uint32_t ImageSize; + uint32_t PluginFlag; +}; + +#pragma pack () + +#define ROM_KERNEL_CMD_RD_MEM 0x0101 +#define ROM_KERNEL_CMD_WR_MEM 0x0202 +#define ROM_KERNEL_CMD_WR_FILE 0x0404 +#define ROM_KERNEL_CMD_ERROR_STATUS 0x0505 +#define RAM_KERNEL_CMD_HEADER 0x0606 +//#define ROM_KERNEL_CMD_RE_ENUM 0x0909 +#define ROM_KERNEL_CMD_DCD_WRITE 0x0A0A +#define ROM_KERNEL_CMD_JUMP_ADDR 0x0B0B +#define ROM_KERNEL_CMD_SKIP_DCD_HEADER 0x0C0C + +#define MAX_DCD_WRITE_REG_CNT 85 +#define ROM_WRITE_ACK 0x128A8A12 +#define ROM_STATUS_ACK 0x88888888 +#define ROM_OK_ACK 0x900DD009 + +#define IVT_BARKER_HEADER 0x402000D1 +#define IVT_BARKER2_HEADER 0x412000D1 + +#define HAB_TAG_DCD 0xd2 /**< Device Configuration Data */ + +class SDPCmdBase:public CmdBase +{ +public: + + enum HAB_t + { + HabUnknown = -1, + HabEnabled = 0x12343412, + HabDisabled = 0x56787856 + }; + + SDPCmdBase(char *p) :CmdBase(p) { init_cmd(); } + +protected: + int check_ack(HIDReport *report, uint32_t ack); + HAB_t get_hab_type(HIDReport *report); + int get_status(HIDReport *p, uint32_t &status, uint8_t report_id); + int init_cmd(); + IvtHeader * search_ivt_header(std::shared_ptr data, size_t &off, size_t limit=ULLONG_MAX); + + std::string m_filename; + SDPCmd m_spdcmd; + uint64_t m_scan_limited = UINT64_MAX; + +private: + int send_cmd(HIDReport *p); + + std::vector m_input; +}; + +class SDPBootlogCmd : public SDPCmdBase +{ +public: + SDPBootlogCmd(char *p); + int run(CmdCtx *) override; +}; + +class SDPDcdCmd : public SDPCmdBase +{ +public: + SDPDcdCmd(char *p); + int run(CmdCtx *) override; + +private: + uint32_t m_dcd_addr; +}; + +class SDPReadMemCmd : public SDPCmdBase +{ +public: + SDPReadMemCmd(char*p); + int run(CmdCtx *) override; + +private: + uint32_t m_mem_addr; + uint8_t m_mem_format; +}; + +class SDPWriteMemCmd : public SDPCmdBase +{ +public: + SDPWriteMemCmd(char*p); + int run(CmdCtx *p) override; + +private: + uint32_t m_mem_addr; + uint8_t m_mem_format; + uint32_t m_mem_value; +}; + +class SDPWriteCmd : public SDPCmdBase +{ +public: + SDPWriteCmd(char*p); + + int run(CmdCtx *p) override; + int run(CmdCtx *p, void *buff, size_t size, uint32_t addr); + +private: + uint32_t m_download_addr; + int32_t m_Ivt; + int m_PlugIn; + uint32_t m_max_download_pre_cmd; + uint32_t m_offset; + bool m_bIvtReserve; + bool m_bskipspl = false; + bool m_bskipfhdr = false; + bool m_bscanterm = false; +}; + +class SDPJumpCmd : public SDPCmdBase +{ +public: + SDPJumpCmd(char*p); + int run(CmdCtx *p) override; + +private: + bool m_clear_dcd = false; + int32_t m_Ivt = -1; + uint32_t m_jump_addr = 0; + bool m_PlugIn = false; +}; + +class SDPSkipDCDCmd :public SDPCmdBase +{ +public: + SDPSkipDCDCmd(char *p); + int run(CmdCtx *p) override; +}; + +class SDPStatusCmd :public SDPCmdBase +{ +public: + SDPStatusCmd(char *p); + int run(CmdCtx *p) override; +}; + +class SDPBootCmd : public SDPCmdBase +{ +public: + SDPBootCmd(char *p); + int run(CmdCtx *p) override; + +private: + bool m_clear_dcd = false; + uint32_t m_dcd_addr = 0; + bool m_nojump = false; +}; diff --git a/libuuu/sdps.cpp b/libuuu/sdps.cpp new file mode 100644 index 0000000..3ea1fa8 --- /dev/null +++ b/libuuu/sdps.cpp @@ -0,0 +1,181 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#include +#include "sdps.h" +#include "hidreport.h" +#include "liberror.h" +#include "libcomm.h" +#include "buffer.h" +#include "sdp.h" +#include "trans.h" + +#include + +//------------------------------------------------------------------------------ +// HID Command Block Wrapper (CBW) +//------------------------------------------------------------------------------ +#pragma pack (1) + +typedef struct _CDBHIDDOWNLOAD { + uint8_t Command; + uint32_t Length; + uint8_t Reserved[11]; +} CDBHIDDOWNLOAD, *PCDBHIDDOWNLOAD; + + +struct _ST_HID_CBW +{ + uint32_t Signature; // Signature: 0x43544C42:1129598018, o "BLTC" (little endian) for the BLTC CBW + uint32_t Tag; // Tag: to be returned in the csw + uint32_t XferLength; // XferLength: number of bytes to transfer + uint8_t Flags; // Flags: + // Bit 7: direction - device shall ignore this bit if the + // XferLength field is zero, otherwise: + // 0 = data-out from the host to the device, + // 1 = data-in from the device to the host. + // Bits 6..0: reserved - shall be zero. + uint8_t Reserved[2]; // Reserved - shall be zero. + CDBHIDDOWNLOAD Cdb; // cdb: the command descriptor block +}; + +#define BLTC_DOWNLOAD_FW 2 +#define HID_BLTC_REPORT_TYPE_DATA_OUT 2 +#define HID_BLTC_REPORT_TYPE_COMMAND_OUT 1 + +#define CBW_BLTC_SIGNATURE 0x43544C42; // "BLTC" (little endian) +#define CBW_PITC_SIGNATURE 0x43544950; // "PITC" (little endian) +// Flags values for _ST_HID_CBW +#define CBW_DEVICE_TO_HOST_DIR 0x80; // "Data Out" +#define CBW_HOST_TO_DEVICE_DIR 0x00; // "Data In" + +#pragma pack () + +#include "rominfo.h" + +int SDPSCmd::run(CmdCtx *pro) +{ + const ROM_INFO * rom = search_rom_info(pro->m_config_item); + if (rom == nullptr) + { + string_ex err; + err.format("%s:%d can't get rom info", __FUNCTION__, __LINE__); + set_last_err_string(err); + return -1; + } + + HIDTrans dev; + if (rom->flags & ROM_INFO_HID_EP1) + dev.set_hid_out_ep(1); + + if(dev.open(pro->m_dev)) + return -1; + + shared_ptr p, p1 = get_file_buffer(m_filename, true); + if (!p1) + return -1; + + HIDReport report(&dev); + report.set_skip_notify(false); + + size_t offset = m_offset; + + vector buff; + + if (m_bscanterm) + { + p = p1->request_data(0, m_scan_limited); + if (IsMBR(p)) + { + size_t pos = 0, length; + length = ScanTerm(p, pos, 512, m_scan_limited); + if (length == 0) + { + set_last_err_string("This wic have NOT terminate tag after bootloader, please use new yocto"); + return -1; + } + + offset = pos - length; + if (offset < 0) + { + set_last_err_string("This wic boot length is wrong"); + return -1; + } + p->resize(pos); + } + } + else + { + p = get_file_buffer(m_filename); //request all data + } + + if (m_bskipflashheader) + offset += GetFlashHeaderSize(p, offset); + + if (offset >= p->size()) + { + set_last_err_string("Offset bigger than file size"); + return -1; + } + + size_t sz = GetContainerActualSize(p, offset, rom->flags & ROM_INFO_HID_ROMAPI); + + if (!(rom->flags & ROM_INFO_HID_NO_CMD)) + { + _ST_HID_CBW cbw; + uint32_t length = (uint32_t) sz; + + memset(&cbw, 0, sizeof(_ST_HID_CBW)); + cbw.Cdb.Command = BLTC_DOWNLOAD_FW; + cbw.Cdb.Length = EndianSwap(length); + + ++cbw.Tag; + cbw.Signature = CBW_BLTC_SIGNATURE; + cbw.XferLength = (uint32_t)length; + cbw.Flags = CBW_HOST_TO_DEVICE_DIR; + + int ret = report.write(&cbw, sizeof(_ST_HID_CBW), HID_BLTC_REPORT_TYPE_COMMAND_OUT); + if (ret) + return ret; + } + + if (rom->flags & ROM_INFO_HID_PACK_SIZE_1020) + report.set_out_package_size(1020); + + int ret = report.write(p->data() + offset, sz, 2); + + if (ret == 0) + { + SDPBootlogCmd log(nullptr); + log.run(pro); + } + + return ret; +} diff --git a/libuuu/sdps.h b/libuuu/sdps.h new file mode 100644 index 0000000..b82f9d7 --- /dev/null +++ b/libuuu/sdps.h @@ -0,0 +1,54 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include "cmd.h" + +class SDPSCmd : public CmdBase +{ +public: + SDPSCmd(char *cmd) :CmdBase(cmd) + { + insert_param_info("boot", nullptr, Param::Type::e_null); + insert_param_info("-f", &m_filename, Param::Type::e_string_filename); + insert_param_info("-offset", &m_offset, Param::Type::e_uint32); + insert_param_info("-skipfhdr", &m_bskipflashheader, Param::Type::e_bool); + insert_param_info("-scanterm", &m_bscanterm, Param::Type::e_bool); + insert_param_info("-scanlimited", &m_scan_limited, Param::Type::e_uint64); + } + int run(CmdCtx *p) override; + +private: + bool m_bskipflashheader=0; + bool m_bscanterm=0; + std::string m_filename; + uint32_t m_offset = 0; + uint64_t m_scan_limited = UINT64_MAX; +}; diff --git a/libuuu/sparse.cpp b/libuuu/sparse.cpp new file mode 100644 index 0000000..263a4be --- /dev/null +++ b/libuuu/sparse.cpp @@ -0,0 +1,224 @@ +/* + * Copyright 2020 NXP. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of the NXP Semiconductor nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "sparse.h" +#include "liberror.h" + +#include +#include + +chunk_header_t * SparseFile::get_next_chunk(uint8_t *p, size_t &pos) +{ + if (pos == 0) + { + sparse_header *pheader = (sparse_header*)p; + if (pheader->magic != SPARSE_HEADER_MAGIC) { + set_last_err_string("Sparse heade Magic missed"); + return nullptr; + } + pos += pheader->file_hdr_sz; + } + + chunk_header_t *pchunk = (chunk_header_t*)(p + pos); + pos += pchunk->total_sz; + return pchunk; +} + +int SparseFile::init_header(size_t blsz, int blcount) +{ + sparse_header header; + + memset(&header, 0, sizeof(header)); + header.magic = SPARSE_HEADER_MAGIC; + header.major_version = 1; + header.minor_version = 0; + header.file_hdr_sz = sizeof(header); + header.chunk_hdr_sz = sizeof(chunk_header); + header.blk_sz = blsz; + m_cur_chunk_header_pos = 0; + if (blcount) + { + m_data.reserve(blsz*blcount + 0x1000); + m_max_size = blsz * blcount; + } + m_data.clear(); + push(&header, sizeof(header)); + m_pcrc = (uint32_t*)(m_data.data() + offsetof(sparse_header, image_checksum)); + return 0; +} + +bool SparseFile::is_append_old_chuck(int type, void *p) +{ + chunk_header_t *pchunk; + pchunk = (chunk_header_t *)(m_data.data() + m_cur_chunk_header_pos); + + if (m_cur_chunk_header_pos == 0) + return false; + + if (pchunk->chunk_type != type) + return false; + + if (type == CHUNK_TYPE_FILL) + { + uint32_t a = *(uint32_t*)(pchunk + 1); + uint32_t b = *(uint32_t*)p; + if (a != b) + return false; + } + return true; +} + +bool SparseFile::is_same_value(void *data, size_t sz) +{ + uint32_t *p = (uint32_t *)data; + uint32_t val = *p; + for (size_t i = 0; i < sz / sizeof(uint32_t); i++) + if (val != p[i]) + return false; + return true; +} + +bool SparseFile::is_validate_sparse_file(void *p, size_t) +{ + sparse_header *pheader = (sparse_header*)p; + if (pheader->magic == SPARSE_HEADER_MAGIC) + return true; + return false; +} + +int SparseFile::push(void *p, size_t sz) +{ + size_t pos = m_data.size(); + m_data.resize(pos + sz); + memcpy(m_data.data() + pos, p, sz); + return 0; +} + +int SparseFile::push_one_block(void *data) +{ + chunk_header_t *pchunk; + pchunk = (chunk_header_t *)(m_data.data() + m_cur_chunk_header_pos); + + sparse_header *pheader; + pheader = (sparse_header *)m_data.data(); + + pheader->total_blks++; + + //int type = is_same_value(data, pheader->blk_sz) ? CHUNK_TYPE_FILL : CHUNK_TYPE_RAW; + int type = CHUNK_TYPE_RAW; + + if (!is_append_old_chuck(type, data)) + { + chunk_header_t header; + header.chunk_type = type; + header.chunk_sz = 1; + header.total_sz = (type == CHUNK_TYPE_FILL) ? sizeof(uint32_t) : pheader->blk_sz; + header.total_sz += sizeof(chunk_header_t); + header.reserved1 = 0; + + pheader->total_chunks++; + + m_cur_chunk_header_pos = m_data.size(); + + push(&header, sizeof(chunk_header_t)); + + if (type == CHUNK_TYPE_RAW) + push(data, pheader->blk_sz); + else + push(data, sizeof(uint32_t)); + } + else + { + pchunk->chunk_sz++; + if (type == CHUNK_TYPE_RAW) + { + push(data, pheader->blk_sz); + pchunk->total_sz += pheader->blk_sz; + } + } + + if (m_data.size() + 2 * pheader->blk_sz > m_max_size ) + return -1; + + return 0; +} + +size_t SparseFile::push_one_chuck(chunk_header_t *p, void *data) +{ + chunk_header_t cheader = *p; + sparse_header *pheader; + pheader = (sparse_header *)m_data.data(); + + size_t sz = p->total_sz - sizeof(chunk_header); + + if (p->total_sz + m_data.size() > m_max_size) + { + if (p->chunk_type == CHUNK_TYPE_RAW) + { + size_t blk = (m_max_size - m_data.size()) / pheader->blk_sz; + if (blk < 2) + return 0; + + blk -= 2; + + cheader.chunk_sz = blk; + sz = blk * pheader->blk_sz; + cheader.total_sz = sizeof(chunk_header_t) + sz; + } + else + return 0; + } + + push(&cheader, sizeof(chunk_header)); + pheader->total_chunks ++; + pheader->total_blks += cheader.chunk_sz; + + if (data) { + push(data, sz); + } + + return sz; +} + +size_t SparseFile::push_raw_data(void *data, size_t sz) +{ + chunk_header_t cheader; + cheader.chunk_type = CHUNK_TYPE_RAW; + + sparse_header *pheader; + pheader = (sparse_header *)m_data.data(); + + cheader.chunk_sz = sz / pheader->blk_sz; + cheader.total_sz = cheader.chunk_sz*pheader->blk_sz + sizeof(chunk_header_t); + pheader = (sparse_header *)m_data.data(); + + return push_one_chuck(&cheader, data); +} diff --git a/libuuu/sparse.h b/libuuu/sparse.h new file mode 100644 index 0000000..7d0af05 --- /dev/null +++ b/libuuu/sparse.h @@ -0,0 +1,60 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#pragma once + +#include "sparse_format.h" + +#include +#include + +class SparseFile +{ +public: + std::vector m_data; + + static chunk_header_t * get_next_chunk(uint8_t *p, size_t &pos); + + int init_header(size_t blsz, int blcount); + + bool is_append_old_chuck(int type, void *p); + bool is_same_value(void *data, size_t sz); + static bool is_validate_sparse_file(void *p, size_t sz); + + int push(void *p, size_t sz); + int push_one_block(void *data); + size_t push_one_chuck(chunk_header_t *p, void *data); + size_t push_raw_data(void *data, size_t sz); + +private: + size_t m_cur_chunk_header_pos; + size_t m_max_size; + uint32_t *m_pcrc; +}; diff --git a/libuuu/sparse_format.h b/libuuu/sparse_format.h new file mode 100644 index 0000000..7347f6d --- /dev/null +++ b/libuuu/sparse_format.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBSPARSE_SPARSE_FORMAT_H_ +#define _LIBSPARSE_SPARSE_FORMAT_H_ + +#include + +typedef uint32_t __le32; +typedef uint16_t __le16; + +#pragma pack(1) + +typedef struct sparse_header { + __le32 magic; /* 0xed26ff3a */ + __le16 major_version; /* (0x1) - reject images with higher major versions */ + __le16 minor_version; /* (0x0) - allow images with higer minor versions */ + __le16 file_hdr_sz; /* 28 bytes for first revision of the file format */ + __le16 chunk_hdr_sz; /* 12 bytes for first revision of the file format */ + __le32 blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ + __le32 total_blks; /* total blocks in the non-sparse output image */ + __le32 total_chunks; /* total chunks in the sparse input image */ + __le32 image_checksum; /* CRC32 checksum of the original data, counting "don't care" */ + /* as 0. Standard 802.3 polynomial, use a Public Domain */ + /* table implementation */ +} sparse_header_t; + +#define SPARSE_HEADER_MAGIC 0xed26ff3a + +#define CHUNK_TYPE_RAW 0xCAC1 +#define CHUNK_TYPE_FILL 0xCAC2 +#define CHUNK_TYPE_DONT_CARE 0xCAC3 +#define CHUNK_TYPE_CRC32 0xCAC4 + +typedef struct chunk_header { + __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ + __le16 reserved1; + __le32 chunk_sz; /* in blocks in output image */ + __le32 total_sz; /* in bytes of chunk input file including chunk header and data */ +} chunk_header_t; + +/* Following a Raw or Fill or CRC32 chunk is data. + * For a Raw chunk, it's the data in chunk_sz * blk_sz. + * For a Fill chunk, it's 4 bytes of the fill data. + * For a CRC32 chunk, it's 4 bytes of CRC32 + */ +#pragma pack() + +#endif diff --git a/libuuu/tar.cpp b/libuuu/tar.cpp new file mode 100644 index 0000000..2473cd5 --- /dev/null +++ b/libuuu/tar.cpp @@ -0,0 +1,133 @@ +/* + * Copyright 2020 NXP. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of the NXP Semiconductor nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include "zlib.h" +#include +#include "buffer.h" +#include "liberror.h" +#include "libuuu.h" +#include +#include +#include "tar.h" +#include +#include +using namespace std; + +int Tar::Open(const string &filename) +{ + bool end_of_file=false; + char end_of_file_blocks[2*TAR_BLOCK_SIZE]; + memset(end_of_file_blocks, 0, sizeof(end_of_file_blocks) ); + m_tarfilename=filename; + + shared_ptr file = get_file_buffer(filename); + if(file == nullptr) + return -1; + + uint8_t* data=file->data(); + uint64_t block_counter=0; + while(!end_of_file) + { + if(!memcmp(end_of_file_blocks,data+block_counter*TAR_BLOCK_SIZE,TAR_BLOCK_SIZE)) + { + end_of_file=true; + break; + } + struct Tar_header* th=(Tar_header*)(data+block_counter*TAR_BLOCK_SIZE); + uint64_t size; + string octal_str((char*)th->size); + //printf("block_counter: %d\n",block_counter ); + //printf("name: %s\n",th->name ); + //printf("signature: %s\n",th->ustar ); + //printf("size: %s\n", th->size); + size=stoll(octal_str, 0, 8); + string name((char*)th->name); + m_filemap[name].size=size; + m_filemap[name].offset=(block_counter+1)*TAR_BLOCK_SIZE; //+1 because the data located right after the header block + m_filemap[name].filename.assign((char*)th->name); + block_counter++; + + //skip the data blocks + uint64_t data_block_num=size/TAR_BLOCK_SIZE; + data_block_num += (size%TAR_BLOCK_SIZE>0)? 1:0; + block_counter+=data_block_num; + } + return 0; +} + +bool Tar::check_file_exist(const string &filename) +{ + + if (m_filemap.find(filename) == m_filemap.end()) + { + string err; + err += "Can't find file "; + err += filename; + set_last_err_string(err); + return false; + } + + return true; +} + + +int Tar::get_file_buff(const string &filename, shared_ptr p ) +{ + + if (m_filemap.find(filename) == m_filemap.end()) + { + string err; + err += "Can't find file "; + err += filename; + set_last_err_string(err); + return -1; + } + + p->resize(m_filemap[filename].size); + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_KNOWN_SIZE); + p->m_request_cv.notify_all(); + + shared_ptr file; + file = get_file_buffer(m_tarfilename); + size_t offset= m_filemap[filename].offset; + size_t size=m_filemap[filename].size; + + p->ref_other_buffer(file, offset, size); + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + + return 0; +} + diff --git a/libuuu/tar.h b/libuuu/tar.h new file mode 100644 index 0000000..19ea05a --- /dev/null +++ b/libuuu/tar.h @@ -0,0 +1,83 @@ +/* + * Copyright 2020 NXP. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of the NXP Semiconductor nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#pragma once + +#include +#include +#include +#include + +class FileBuffer; + +#define TAR_BLOCK_SIZE 512 + +#pragma pack(1) +struct Tar_header +{ + uint8_t name[100]; + uint8_t mode[8]; + uint8_t owner_id[8]; + uint8_t group_id[8]; + uint8_t size[12]; + uint8_t modi_time[12]; + uint8_t checksum[8]; + uint8_t type[1]; + uint8_t linkname[100]; + uint8_t ustar[6]; + uint8_t version[2]; + uint8_t uname[32]; + uint8_t gname[32]; + uint8_t major_num[8]; + uint8_t minor_num[8]; + uint8_t prefix[155]; +}; +#pragma pack() + +class Tar_file_Info +{ +public: + std::string filename; + uint64_t offset; + uint64_t size; +}; + + +class Tar +{ + std::string m_tarfilename; + +public: + std::map m_filemap; + int Open(const std::string &filename); + bool check_file_exist(const std::string &filename); + int get_file_buff(const std::string &filename, std::shared_ptr p); +}; diff --git a/libuuu/trans.cpp b/libuuu/trans.cpp new file mode 100644 index 0000000..ed11235 --- /dev/null +++ b/libuuu/trans.cpp @@ -0,0 +1,299 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include "trans.h" +#include "libuuu.h" +#include "liberror.h" +#include "libusb.h" + +extern "C" +{ +#include "libusb.h" +} + +using namespace std; + +TransBase::~TransBase() +{ +} + +int TransBase::read(vector &buff) +{ + size_t size; + const auto ret = read(buff.data(), buff.size(), &size); + if (ret) + return ret; + buff.resize(size); + return ret; +} + +int USBTrans::open(void *p) +{ + m_devhandle = p; + libusb_device_handle * handle = (libusb_device_handle *)m_devhandle; + if (libusb_kernel_driver_active(handle, 0)) + { + int ret = libusb_detach_kernel_driver((libusb_device_handle *)m_devhandle, 0); + if(ret <0 && ret != LIBUSB_ERROR_NOT_SUPPORTED) + { + set_last_err_string("detach kernel driver failure"); + return -1; + } + } + + if (libusb_claim_interface(handle, 0)) + { + set_last_err_string("Failure claim interface"); + return -1; + } + + libusb_config_descriptor *config; + if (libusb_get_active_config_descriptor(libusb_get_device(handle), &config)) + { + set_last_err_string("Can't get config descriptor"); + return -1; + } + + m_EPs.clear(); + for (int i = 0; i < config->interface[0].altsetting[0].bNumEndpoints; i++) + { + m_EPs.push_back(EPInfo(config->interface[0].altsetting[0].endpoint[i].bEndpointAddress, + config->interface[0].altsetting[0].endpoint[i].wMaxPacketSize)); + }; + + libusb_free_config_descriptor(config); + + return 0; +} + +int USBTrans::close() +{ + /* needn't clean resource here + libusb_close will release all resource when finish running cmd + */ + return 0; +} + +int HIDTrans::open(void *p) +{ + if (USBTrans::open(p)) + return -1; + + for (const auto &ep : m_EPs) + { + if (ep.addr > 0 && ((ep.addr & 0x80) == 0)) + m_outEP = ep.addr; + } + + return 0; +} + +int HIDTrans::write(void *buff, size_t size) +{ + int ret; + uint8_t *p = (uint8_t *)buff; + int actual_size; + if (m_outEP) + { + ret = libusb_interrupt_transfer( + (libusb_device_handle *)m_devhandle, + m_outEP, + p, + size, + &actual_size, + 1000 + ); + } + else + { + ret = libusb_control_transfer( + (libusb_device_handle *)m_devhandle, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE, + m_set_report, + (2 << 8) | p[0], + 0, + p, + size, + 1000 + ); + } + + if (ret < 0) + { + string err; + err = "HID(W):"; + err += libusb_error_name(ret); + set_last_err_string(err); + return ret; + } + + return ret; +} + +int HIDTrans::read(void *buff, size_t size, size_t *rsize) +{ + int ret; + int actual; + ret = libusb_interrupt_transfer( + (libusb_device_handle *)m_devhandle, + 0x81, + (uint8_t*)buff, + size, + &actual, + m_read_timeout + ); + + *rsize = actual; + + if (ret < 0) + { + string error; + string err; + err = "HID(R):"; + err += libusb_error_name(ret); + set_last_err_string(err); + return ret; + } + + return 0; +} + +int BulkTrans::write(void *buff, size_t size) +{ + int ret; + int actual_lenght; + for (size_t i = 0; i < size; i += m_MaxTransPreRequest) + { + uint8_t *p = (uint8_t *)buff; + p += i; + size_t sz; + sz = size - i; + if (sz > m_MaxTransPreRequest) + sz = m_MaxTransPreRequest; + + ret = libusb_bulk_transfer( + (libusb_device_handle *)m_devhandle, + m_ep_out.addr, + p, + sz, + &actual_lenght, + m_timeout + ); + + if (ret < 0) + { + string error; + string err; + err = "Bulk(W):"; + err += libusb_error_name(ret); + set_last_err_string(err); + return ret; + } + } + + //Send zero package + if (m_b_send_zero && ( (size%m_ep_out.package_size) == 0)) + { + ret = libusb_bulk_transfer( + (libusb_device_handle *)m_devhandle, + m_ep_out.addr, + nullptr, + 0, + &actual_lenght, + 2000 + ); + + if (ret < 0) + { + string error; + string err; + err = "Bulk(W):"; + err += libusb_error_name(ret); + set_last_err_string(err); + return ret; + } + } + + return ret; +} + +int BulkTrans::open(void *p) +{ + if (USBTrans::open(p)) + return -1; + + for (size_t i = 0; i < m_EPs.size(); i++) + { + if (m_EPs[i].addr > 0) + { + if ((m_EPs[0].addr & 0x80) && m_ep_in.addr == 0) + m_ep_in = m_EPs[i]; + else if (m_ep_out.addr == 0) + m_ep_out = m_EPs[i]; + } + } + return 0; +} +int BulkTrans::read(void *buff, size_t size, size_t *rsize) +{ + int ret; + int actual_lenght; + uint8_t *p = (uint8_t *)buff; + + if (size == 0) + { + *rsize = 0; + return 0; + } + + ret = libusb_bulk_transfer( + (libusb_device_handle *)m_devhandle, + m_ep_in.addr, + p, + size, + &actual_lenght, + m_timeout + ); + + *rsize = actual_lenght; + + if (ret < 0) + { + string error; + string err; + err = "Bulk(R):"; + err += libusb_error_name(ret); + set_last_err_string(err); + return ret; + } + + return ret; +} diff --git a/libuuu/trans.h b/libuuu/trans.h new file mode 100644 index 0000000..3f137d4 --- /dev/null +++ b/libuuu/trans.h @@ -0,0 +1,109 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#pragma once + +#include +#include +#include + +class TransBase +{ +public: + TransBase() = default; + TransBase(const TransBase&) = delete; + TransBase& operator=(const TransBase&) = delete; + virtual ~TransBase(); + + virtual int open(void *) { return 0; } + virtual int close() { return 0; } + virtual int write(void *buff, size_t size) = 0; + virtual int read(void *buff, size_t size, size_t *return_size) = 0; + int write(std::vector & buff) { return write(buff.data(), buff.size()); } + int read(std::vector &buff); + +protected: + void * m_devhandle = nullptr; +}; + +class EPInfo +{ +public: + constexpr EPInfo() = default; + constexpr EPInfo(int a, int size) : addr{a}, package_size{size} {} + int addr = 0; + int package_size = 64; +}; + +class USBTrans : public TransBase +{ +public: + int open(void *p) override; + int close() override; + +protected: + std::vector m_EPs; +}; +class HIDTrans : public USBTrans +{ +public: + HIDTrans(int read_timeout = 1000) : m_read_timeout{read_timeout} {} + ~HIDTrans() override { if (m_devhandle) close(); m_devhandle = nullptr; } + + int open(void *p) override; + void set_hid_out_ep(int ep) noexcept { m_outEP = ep; } + int write(void *buff, size_t size) override; + int read(void *buff, size_t size, size_t *return_size) override; + +private: + int m_outEP = 0; + const int m_read_timeout = 1000; + int m_set_report = 9; +}; + +class BulkTrans : public USBTrans +{ +public: + BulkTrans(uint64_t timeout = 2000) : m_timeout{timeout} {} + ~BulkTrans() override { if (m_devhandle) close(); m_devhandle = nullptr; } + + int open(void *p) override; + int write(void *buff, size_t size) override; + int read(void *buff, size_t size, size_t *return_size) override; + +private: + size_t m_MaxTransPreRequest = 0x100000; + int m_b_send_zero = 0; + EPInfo m_ep_in; + EPInfo m_ep_out; + uint64_t m_timeout = 2000; +}; + +int polling_usb(std::atomic& bexit); diff --git a/libuuu/usbhotplug.cpp b/libuuu/usbhotplug.cpp new file mode 100644 index 0000000..801bc76 --- /dev/null +++ b/libuuu/usbhotplug.cpp @@ -0,0 +1,564 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +/* + * Windows libusb don't support hotplug yet + * Will polling devices list every 100ms + */ + +#include +#include +#include +#include +#include +#include +#include "libusb.h" +#include "liberror.h" +#include "config.h" +#include "cmd.h" +#include "libcomm.h" +#include "libuuu.h" +#include "vector" +#include + +using chrono::milliseconds; +using chrono::operator ""ms; +using chrono::seconds; +using chrono::operator ""s; + +static atomic g_wait_usb_timeout{-1s}; +static atomic g_usb_poll_period{200ms}; +static atomic g_wait_next_usb_timeout{-1s}; + +enum KnownDeviceState { + NoKnownDevice, + KnownDeviceToDo, + KnownDeviceDone, + WaitNextKnownDevice, +}; +static atomic g_known_device_state{NoKnownDevice}; + +class CAutoDeInit +{ +public: + CAutoDeInit() + { + if (libusb_init(nullptr) < 0) + throw runtime_error{ "Call libusb_init failure" }; + } + ~CAutoDeInit() + { + libusb_exit(nullptr); + } +} g_autoDeInit; + +class CAutoList +{ +public: + libusb_device **list = nullptr; + + CAutoList(libusb_device **list) + { + this->list = list; + m_rc = -1; + } + + CAutoList(CAutoList &&other) + { + this->list = other.list; + this->m_rc = other.m_rc; + other.list = nullptr; + } + + CAutoList() + { + m_rc = libusb_get_device_list(nullptr, &list); + if (m_rc < 0) { + set_last_err_string(std::string("libusb_get_device_list failed: ") + + libusb_strerror(static_cast(m_rc))); + } + } + + ~CAutoList() + { + if (list != nullptr) { + libusb_free_device_list(list, 1); + } + } + + CAutoList& operator=(CAutoList &&other) + { + this->list = other.list; + this->m_rc = other.m_rc; + other.list = nullptr; + return *this; + } + + CAutoList& operator=(const CAutoList&) = delete; // Prevent copy, allow move only + CAutoList(const CAutoList&) = delete; // Prevent copy, allow move only + + bool good() const + { + return m_rc >= 0; + } + +private: + int m_rc = 0; +}; + +static struct { + vector list; + mutex lock; + + void push_back(string filter) + { + lock_guard guard{lock}; + list.emplace_back(move(filter)); + } + + bool is_valid(const string& path) + { + lock_guard guard{lock}; + if (list.empty()) + return true; + + auto end = list.end(); + auto pos = find(list.begin(), end, path); + return pos != end; + } +} g_filter_usbpath; + +struct Timer +{ + using Clock = chrono::steady_clock; + Clock::time_point start; + + explicit Timer(Clock::time_point start) : start{start} {} + Timer() : Timer{Clock::now()} {} + + bool is_elapsed(Clock::duration interval) const + { + return (Clock::now() - start) >= interval; + } + + void reset(Clock::time_point start) + { + this->start = start; + } + + void reset() + { + reset(Clock::now()); + } +}; + +#ifdef _MSC_VER +#define TRY_SUDO +#else +#define TRY_SUDO ",Try sudo uuu" +#endif + +static string get_device_path(libusb_device *dev) +{ + uint8_t path[8]; + + int bus = libusb_get_bus_number(dev); + + string_ex str; + + str.format("%d:", bus); + + int ret = libusb_get_port_numbers(dev, path, sizeof(path)); + if (ret < 0) + return ""; + + string_ex s; + s.format("%d", path[0]); + str.append(s); + + for (int j = 1; j < ret; j++) + { + s.format("%d", path[j]); + str.append(s); + } + return str; +} + +static int open_libusb(libusb_device *dev, void **usb_device_handle) +{ + int retry = 1; +#ifdef WIN32 + retry = 5; +#endif + + while (retry) + { + retry--; + + /* work around windows open device failure 1/10 + * sometime HID device detect need some time, refresh list + * to make sure HID driver installed. + */ + CAutoList l; + + int ret; + if ((ret = libusb_open(dev, (libusb_device_handle **)(usb_device_handle))) < 0) + { + if ((ret != LIBUSB_ERROR_NOT_SUPPORTED) || (retry == 0)) + { + set_last_err_string("Failure open usb device" TRY_SUDO); + return -1; + } + this_thread::sleep_for(200ms); + } + else + { + return 0; + } + } + + return -1; +} + +/** + Thread function. Didn't call this function directly. + Unbalance libusb_unref_device. + Before start thread, need call libusb_ref_device to dev is free + + libusb_get_list() + libusb_ref_devive // avoid free at libusb_free_list if run_usb_cmd have not open device in time. + thread start run_usb_cmds; + libusb_free_list() +*/ +static int run_usb_cmds(ConfigItem *item, libusb_device *dev, short bcddevice) +{ + int ret; + uuu_notify nt; + nt.type = uuu_notify::NOFITY_DEV_ATTACH; + + string str; + str = get_device_path(dev); + nt.str = (char*)str.c_str(); + call_notify(nt); + + CmdUsbCtx ctx; + ctx.m_config_item = item; + ctx.m_current_bcd = bcddevice; + + if (ret = open_libusb(dev, &(ctx.m_dev))) + { + nt.type = uuu_notify::NOTIFY_CMD_END; + nt.status = -1; + call_notify(nt); + return ret; + } + + ret = run_cmds(item->m_protocol.c_str(), &ctx); + g_known_device_state = KnownDeviceDone; + + nt.type = uuu_notify::NOTIFY_THREAD_EXIT; + call_notify(nt); + + libusb_unref_device(dev); //ref_device when start thread + clear_env(); + return ret; +} + +static int usb_add(libusb_device *dev) +{ + struct libusb_device_descriptor desc; + int r = libusb_get_device_descriptor(dev, &desc); + if (r < 0) { + set_last_err_string("failure get device descrior"); + return r; + } + + string str; + str = get_device_path(dev); + if (!g_filter_usbpath.is_valid(str)) + return -1; + + ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice); + this_thread::sleep_for(g_usb_poll_period.load()); + + if (item) + { + g_known_device_state = KnownDeviceToDo; + + /* + * start new thread, need increase dev ref number. + * otherwise polling thread, free_device_list free device if open device call after free_device_list. + */ + libusb_ref_device(dev); + + std::thread(run_usb_cmds, item, dev, desc.bcdDevice).detach(); + } + return 0; +} + +static int usb_remove(libusb_device * /*dev*/) +{ + + return 0; +} + +void compare_list(libusb_device ** old, libusb_device **nw) +{ + libusb_device * dev; + int i = 0; + + if (old == nullptr) + { + while ((dev = nw[i++]) != nullptr) + { + usb_add(dev); + } + return; + } + + while ((dev = nw[i++]) != nullptr) + { + libusb_device * p; + int j = 0; + while ((p = old[j++]) != nullptr) + { + if (p == dev) + break;//find it. + }; + if (p != dev) + usb_add(dev); + } + + i = 0; + while ((dev = old[i++]) != nullptr) + { + libusb_device * p; + int j = 0; + while ((p = nw[j++]) != nullptr) + { + if (p == dev) + break;//find it. + }; + if (p != dev) + usb_remove(dev); + } +} + +static int check_usb_timeout(Timer& usb_timer) +{ + auto known_device_state = g_known_device_state.load(); + if (known_device_state == KnownDeviceDone) + { + g_known_device_state = known_device_state = WaitNextKnownDevice; + usb_timer.reset(); + } + + auto usb_timeout = g_wait_usb_timeout.load(); + if (usb_timeout >= 0s && known_device_state == NoKnownDevice) + { + if (usb_timer.is_elapsed(usb_timeout)) + { + set_last_err_string("Timeout: Wait for Known USB Device"); + return -1; + } + } + + usb_timeout = g_wait_next_usb_timeout.load(); + if (usb_timeout >= 0s && g_known_device_state == WaitNextKnownDevice) + { + if (usb_timer.is_elapsed(usb_timeout)) + { + set_last_err_string("Timeout: Wait for next USB Device"); + return -1; + } + } + + return 0; +} + +int polling_usb(std::atomic& bexit) +{ + if (run_cmds("CFG:", nullptr)) + return -1; + + Timer usb_timer; + + CAutoList oldlist(nullptr); + + while(!bexit) + { + CAutoList newlist; + if (!newlist.good()) + { + return -1; + } + + compare_list(oldlist.list, newlist.list); + + std::swap(oldlist, newlist); + + this_thread::sleep_for(g_usb_poll_period.load()); + + if (check_usb_timeout(usb_timer)) + return -1; + } + + return 0; +} + +CmdUsbCtx::~CmdUsbCtx() +{ + if (m_dev) + { + libusb_close((libusb_device_handle*)m_dev); + m_dev = 0; + } +} + +int CmdUsbCtx::look_for_match_device(const char *pro) +{ + if (run_cmds("CFG:", nullptr)) + return -1; + + Timer usb_timer; + + while (1) + { + CAutoList l; + + if (!l.good()) { + break; + } + + size_t i = 0; + libusb_device *dev; + + while ((dev = l.list[i++]) != nullptr) + { + struct libusb_device_descriptor desc; + int r = libusb_get_device_descriptor(dev, &desc); + if (r < 0) { + set_last_err_string("failure get device descrior"); + return -1; + } + string str = get_device_path(dev); + + if (!g_filter_usbpath.is_valid(str)) + continue; + + ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice); + if (item && item->m_protocol == str_to_upper(pro)) + { + uuu_notify nt; + nt.type = uuu_notify::NOFITY_DEV_ATTACH; + m_config_item = item; + m_current_bcd = desc.bcdDevice; + + int ret; + if (ret = open_libusb(dev, &(m_dev))) + return ret; + + nt.str = (char*)str.c_str(); + call_notify(nt); + + return 0; + } + } + + this_thread::sleep_for(200ms); + + uuu_notify nt; + nt.type = nt.NOTIFY_WAIT_FOR; + nt.str = (char*)"Wait for Known USB"; + call_notify(nt); + + check_usb_timeout(usb_timer); + } + + return -1; +} + +int uuu_add_usbpath_filter(const char *path) +{ + g_filter_usbpath.push_back(path); + return 0; +} + +int uuu_for_each_devices(uuu_ls_usb_devices fn, void *p) +{ + CAutoList l; + size_t i = 0; + libusb_device *dev; + + if (!l.good()) { + return -1; + } + + while ((dev = l.list[i++]) != nullptr) + { + struct libusb_device_descriptor desc; + int r = libusb_get_device_descriptor(dev, &desc); + if (r < 0) { + set_last_err_string("failure get device descrior"); + return -1; + } + string str = get_device_path(dev); + + ConfigItem *item = get_config()->find(desc.idVendor, desc.idProduct, desc.bcdDevice); + if (item) + { + if (fn(str.c_str(), item->m_chip.c_str(), item->m_protocol.c_str(), desc.idVendor, desc.idProduct, desc.bcdDevice, p)) + { + set_last_err_string("call back return error"); + return -1; + } + } + } + + return 0; +} + +int uuu_set_wait_timeout(int timeout_in_seconds) +{ + g_wait_usb_timeout = seconds{timeout_in_seconds}; + return 0; +} + +void uuu_set_poll_period(int period_in_milliseconds) +{ + g_usb_poll_period = milliseconds{period_in_milliseconds}; +} + +int uuu_set_wait_next_timeout(int timeout_in_seconds) +{ + g_wait_next_usb_timeout = seconds{timeout_in_seconds}; + return 0; +} diff --git a/libuuu/version.cpp b/libuuu/version.cpp new file mode 100644 index 0000000..229c5ef --- /dev/null +++ b/libuuu/version.cpp @@ -0,0 +1,70 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include "gitversion.h" +#include "libuuu.h" + +#include + +using namespace std; + +static constexpr auto g_version = GIT_VERSION; + +const char *uuu_get_version_string() +{ + return g_version; +} + +int uuu_get_version() +{ + string version_str{g_version}; + + // Find first dot because major version number must be before it + auto pos = version_str.find("."); + // Find the position of the character right before the start of the number + auto vs = version_str.find_last_not_of("0123456789", pos - 1); + // Let "vs" point exactly to the first character of the major version number + ++vs; + + string temp_num_str = version_str.substr(vs, pos - vs); + const auto maj = static_cast(stoll(temp_num_str, nullptr, 10)); + + version_str = version_str.substr(pos + 1); + pos = version_str.find("."); + temp_num_str = version_str.substr(0, pos); + const auto min = static_cast(stoll(temp_num_str, nullptr, 10)); + + version_str = version_str.substr(pos + 1); + temp_num_str = version_str.substr(0, pos = version_str.find("-")); + const auto build = static_cast(stoll(temp_num_str, nullptr, 10)); + + return (maj << 24) | (min << 12) | build; +} diff --git a/libuuu/zip.cpp b/libuuu/zip.cpp new file mode 100644 index 0000000..08dc923 --- /dev/null +++ b/libuuu/zip.cpp @@ -0,0 +1,330 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#include +#include +#include +#include "buffer.h" +#include "libcomm.h" +#include "libuuu.h" +#include "liberror.h" +#include "zip.h" + +#define CHUNK 0x10000 + +int Zip::BuildDirInfo() +{ + shared_ptr zipfile = get_file_buffer(m_filename); + if (zipfile == nullptr) + { + return -1; + } + + size_t i; + Zip_eocd *peocd = nullptr; + Zip64_eocd_locator *peocd64_loc = nullptr; + Zip64_eocd *peocd64 = nullptr; + + for (i = zipfile->size() - sizeof(Zip_eocd); i > 0; i--) + { + peocd = (Zip_eocd*)(zipfile->data() + i); + if (peocd->sign == EOCD_SIGNATURE) + { + if (peocd->offset_of_central_dir == 0xFFFFFFFF) + {//zip64 + for (size_t j = i - sizeof(Zip64_eocd_locator); j > 0; j--) + { + peocd64_loc = (Zip64_eocd_locator*)(zipfile->data() + j); + if (peocd64_loc->sign == EOCD64_LOCATOR_SIGNATURE) + { + peocd64 = (Zip64_eocd*)(zipfile->data() + peocd64_loc->offset_of_eocd); + if (peocd64->sign != EOCD64_SIGNATURE) + { + set_last_err_string("Can't find EOCD64_SIGNATURE, not a zip64 file"); + return -1; + } + break; + } + + if (zipfile->size() - j > 0x10000) + { + set_last_err_string("Can't find EOCD, not a zip file"); + return -1; + } + } + } + break; + } + + if (zipfile->size() - i > 0x10000) + { + set_last_err_string("Can't find EOCD, not a zip file"); + return -1; + } + } + + if (peocd == 0) + { + set_last_err_string("Can't find EOCD, not a zip file"); + return -1; + } + + i = peocd64? peocd64->offset:peocd->offset_of_central_dir; + size_t total = i; + total += peocd64 ? peocd64->size: peocd->size_of_central_dir; + + while (i < total) + { + Zip_central_dir *pdir = (Zip_central_dir *)(zipfile->data() + i); + if (pdir->sign != DIR_SIGNTURE) + { + set_last_err_string("DIR signature missmatched"); + return -1; + } + Zip_file_Info info; + info.m_filename.append((char*)pdir->filename, pdir->file_name_length); + info.m_offset = pdir->offset; + info.m_filesize = pdir->uncompressed_size; + info.m_timestamp = (pdir->last_modidfy_date << 16) + pdir->last_modidfy_time; + info.m_compressedsize = pdir->compressed_size; + + if (pdir->extrafield_length) + { + size_t e; + for (e = 0; e < pdir->extrafield_length; /*dummy*/) + { + Zip_ext *ext = (Zip_ext*)(zipfile->data() + e + i + sizeof(Zip_central_dir) + pdir->file_name_length); + + if (ext->tag == 0x1) + { + size_t cur64 = 0; + if (info.m_filesize == 0xFFFFFFFF) + { + info.m_filesize = *((uint64_t*)(((uint8_t*)ext) + sizeof(Zip_ext) + cur64)); + cur64 += 8; + } + + if (cur64 > ext->size) + { + set_last_err_string("error pass zip64"); + return -1; + } + + if (info.m_compressedsize == 0xFFFFFFFF) + { + info.m_compressedsize = *((uint64_t*)(((uint8_t*)ext) + sizeof(Zip_ext) + cur64)); + cur64 += 8; + } + + if (cur64 > ext->size) + { + set_last_err_string("error pass zip64"); + return -1; + } + + if (info.m_offset == 0xFFFFFFFF) + { + info.m_offset = *((uint64_t*)(((uint8_t*)ext) + sizeof(Zip_ext) + cur64)); + cur64 += 8; + } + + if (cur64 > ext->size) + { + set_last_err_string("error pass zip64"); + return -1; + } + + break; + } + e += ext->size + sizeof(Zip_ext); + } + } + i += sizeof(Zip_central_dir) + pdir->extrafield_length + pdir->file_name_length + pdir->file_comment_length; + m_filemap[info.m_filename] = info; + } + + return 0; +} + +bool Zip::check_file_exist(string filename) +{ + if (m_filemap.find(filename) == m_filemap.end()) + { + string err; + err += "Can't find file "; + err += filename; + set_last_err_string(err); + return false; + } + + return true; +} + +int Zip::get_file_buff(string filename, shared_ptr p) +{ + if (m_filemap.find(filename) == m_filemap.end()) + { + string err; + err += "Can't find file "; + err += filename; + set_last_err_string(err); + return -1; + } + + uuu_notify ut; + ut.type = uuu_notify::NOTIFY_DECOMPRESS_START; + ut.str = (char*)filename.c_str(); + call_notify(ut); + + return m_filemap[filename].decompress(this, p); +} + +int Zip::Open(string filename) +{ + m_filename = filename; + return BuildDirInfo(); +} + +Zip_file_Info::Zip_file_Info() +{ + +} + +Zip_file_Info::~Zip_file_Info() +{ + memset(&m_strm, 0, sizeof(m_strm)); +} + +int Zip_file_Info::decompress(Zip *pZip, shared_ptrp) +{ + p->resize(m_filesize); + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_KNOWN_SIZE); + p->m_request_cv.notify_all(); + + uuu_notify ut; + ut.type = uuu_notify::NOTIFY_DECOMPRESS_SIZE; + ut.total = m_filesize; + call_notify(ut); + size_t lastpos = 0; + + shared_ptr zipfile = get_file_buffer(pZip->get_filename()); + if (zipfile == nullptr) + return -1; + + Zip_file_desc *file_desc=(Zip_file_desc *)(zipfile->data() + m_offset); + if (file_desc->sign != FILE_SIGNATURE) + { + set_last_err_string("file signature miss matched"); + return -1; + } + + size_t off = sizeof(Zip_file_desc) + file_desc->file_name_length + file_desc->extrafield_length; + + if (file_desc->compress_method == 0) + { + p->ref_other_buffer(zipfile, m_offset + off, m_filesize); + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + return 0; + } + + if (file_desc->compress_method != 8) + { + set_last_err_string("Unsupport compress method"); + return -1; + } + + int ret; + size_t pos = 0; + + memset(&m_strm, 0, sizeof(m_strm)); + inflateInit2(&m_strm, -MAX_WBITS); + + /* decompress until deflate stream ends or end of file */ + m_strm.avail_in = m_compressedsize; + m_strm.next_in = zipfile->data() + m_offset + off; + m_strm.total_in = m_compressedsize; + + /* run inflate() on input until output buffer not full */ + size_t each_out_size = CHUNK; + do { + + if (p->size() - pos < each_out_size) + each_out_size = p->size() - pos; + + m_strm.avail_out = each_out_size; + m_strm.next_out = p->data() + pos; + ret = inflate(&m_strm, Z_NO_FLUSH); + //assert(ret != Z_STREAM_ERROR); /* state not clobbered */ + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; /* and fall through */ + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&m_strm); + return -1; + } + size_t have = each_out_size - m_strm.avail_out; + + p->m_avaible_size = pos; + p->m_request_cv.notify_all(); + + pos += have; + + if (pos - lastpos > 100 * 1024 * 1024) + { + uuu_notify ut; + ut.type = uuu_notify::NOTIFY_DECOMPRESS_POS; + ut.index = pos; + call_notify(ut); + lastpos = pos; + } + + } while (ret != Z_STREAM_END); + + /* clean up and return */ + (void)inflateEnd(&m_strm); + + if (ret != Z_STREAM_END) + { + set_last_err_string("decompress error"); + return -1; + } + + p->m_avaible_size = m_filesize; + atomic_fetch_or(&p->m_dataflags, FILEBUFFER_FLAG_LOADED); + p->m_request_cv.notify_all(); + + ut.type = uuu_notify::NOTIFY_DECOMPRESS_POS; + ut.index = m_filesize; + call_notify(ut); + + return 0; +} diff --git a/libuuu/zip.h b/libuuu/zip.h new file mode 100644 index 0000000..37676bc --- /dev/null +++ b/libuuu/zip.h @@ -0,0 +1,192 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ +#pragma once + +#include "backfile.h" + +#include "zlib.h" + +#include +#include +#include + +class FileBuffer; + +#pragma pack(1) +struct Zip_data_desc +{ + uint32_t sign; + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; +}; + +struct Zip_file_desc +{ + uint32_t sign; + uint16_t version_mini_extract; + uint16_t flags; + uint16_t compress_method; + uint16_t last_modidfy_time; + uint16_t last_modidfy_date; + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t file_name_length; + uint16_t extrafield_length; + uint8_t filename[0]; +}; +struct Zip_central_dir +{ + uint32_t sign; + uint16_t version; + uint16_t version_mini_extract; + uint16_t flags; + uint16_t compress_method; + uint16_t last_modidfy_time; + uint16_t last_modidfy_date; + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t file_name_length; + uint16_t extrafield_length; + uint16_t file_comment_length; + uint16_t disk_number; + uint16_t internal_file_attr; + uint32_t external_file_attr; + uint32_t offset; + uint8_t filename[0]; +}; + +struct Zip64_central_dir +{ + uint32_t sign; + uint16_t version; + uint16_t version_mini_extract; + uint16_t flags; + uint16_t compress_method; + uint16_t last_modidfy_time; + uint16_t last_modidfy_date; + uint32_t crc; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t file_name_length; + uint16_t extrafield_length; + uint16_t file_comment_length; + uint16_t disk_number; + uint16_t internal_file_attr; + uint32_t external_file_attr; + uint32_t offset; + uint8_t filename[0]; +}; +struct Zip_eocd +{ + uint32_t sign; + uint16_t num_of_thisdisk; + uint16_t start_disk_of_dir; + uint16_t num_of_dir_ondisk; + uint16_t num_of_dir; + uint32_t size_of_central_dir; + uint32_t offset_of_central_dir; + uint16_t length_of_comment; + uint8_t comment[0]; +}; + +struct Zip64_eocd_locator +{ + uint32_t sign; + uint32_t num_of_thisdisk; + uint64_t offset_of_eocd; + uint32_t total_num_disks; +}; + +struct Zip64_eocd +{ + uint32_t sign; + uint64_t size_of_eocd; + uint16_t version; + uint16_t version_mini_extract; + uint32_t num_of_dir_ondisk; + uint32_t num_of_disk; + uint64_t total_ondisk; + uint64_t total; + uint64_t size; + uint64_t offset; +}; + +struct Zip_ext +{ + uint16_t tag; + uint16_t size; +}; + +#define EOCD_SIGNATURE 0x06054b50 +#define DIR_SIGNTURE 0x02014b50 +#define DATA_SIGNATURE 0x08074b50 +#define FILE_SIGNATURE 0x04034b50 +#define EOCD64_LOCATOR_SIGNATURE 0x07064b50 +#define EOCD64_SIGNATURE 0x06064b50 + +class Zip; + +class Zip_file_Info +{ +public: + Zip_file_Info(); + ~Zip_file_Info(); + + int decompress(Zip *pZip, std::shared_ptr p); + +private: + std::string m_filename; + uint32_t m_timestamp; + size_t m_filesize; + size_t m_compressedsize; + size_t m_offset; + z_stream m_strm; + bool m_decompressed; + + friend Zip; +}; + +class Zip : public Backfile +{ +public: + int BuildDirInfo(); + bool check_file_exist(std::string filename); + int get_file_buff(std::string filename, std::shared_ptrp); + int Open(std::string filename); + + std::map m_filemap; +}; + + +#pragma pack() diff --git a/msvc/bzip2.vcxproj b/msvc/bzip2.vcxproj new file mode 100644 index 0000000..41c6cb8 --- /dev/null +++ b/msvc/bzip2.vcxproj @@ -0,0 +1,227 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + NotUsing + NotUsing + NotUsing + NotUsing + + + + 15.0 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF} + Win32Proj + bzip2 + 10.0.16299.0 + + + + StaticLibrary + true + v141 + Unicode + + + StaticLibrary + false + v141 + true + Unicode + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + $(SolutionDir)$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)$(Platform)\$(Configuration)\ + + + false + + + + Use + Level3 + Disabled + false + _DEBUG;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS; _CRT_NONSTDC_NO_DEPRECATE;;_CRT_SECURE_NO_WARNINGS; _CRT_NONSTDC_NO_DEPRECATE; + true + + + Windows + true + + + true + + + + + Use + Level3 + Disabled + false + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) _CRT_SECURE_NO_WARNINGS setmode=_setmode;_CRT_SECURE_NO_WARNINGS; _CRT_NONSTDC_NO_DEPRECATE; + true + + + Windows + true + + + + + Use + Level3 + MaxSpeed + true + true + false + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) _CRT_SECURE_NO_WARNINGS setmode=_setmode;_CRT_SECURE_NO_WARNINGS; _CRT_NONSTDC_NO_DEPRECATE; + true + MultiThreaded + + + Windows + true + true + true + + + + + Use + Level3 + MaxSpeed + true + true + false + NDEBUG;_LIB;%(PreprocessorDefinitions) _CRT_SECURE_NO_WARNINGS setmode=_setmode;_CRT_SECURE_NO_WARNINGS; _CRT_NONSTDC_NO_DEPRECATE; + true + MultiThreaded + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/msvc/bzip2.vcxproj.filters b/msvc/bzip2.vcxproj.filters new file mode 100644 index 0000000..1dc7d08 --- /dev/null +++ b/msvc/bzip2.vcxproj.filters @@ -0,0 +1,52 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/msvc/createversion.bat b/msvc/createversion.bat new file mode 100644 index 0000000..0e09573 --- /dev/null +++ b/msvc/createversion.bat @@ -0,0 +1,16 @@ +@echo off + +call git --version +IF ERRORLEVEL 1 ( + echo build from tarball +) ELSE ( + IF "%APPVEYOR_BUILD_VERSION%" == "" ( + echo build not from appveryor + ) ELSE ( + git tag uuu_%APPVEYOR_BUILD_VERSION% + ) + + FOR /F "tokens=*" %%a in ('call git describe --tags --long') do ( + echo #define GIT_VERSION "lib%%a" > %1/gitversion.h + ) +) \ No newline at end of file diff --git a/msvc/libuuu.filters b/msvc/libuuu.filters new file mode 100644 index 0000000..92cb56c --- /dev/null +++ b/msvc/libuuu.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/msvc/libuuu.vcxproj b/msvc/libuuu.vcxproj new file mode 100644 index 0000000..6a84e20 --- /dev/null +++ b/msvc/libuuu.vcxproj @@ -0,0 +1,218 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 15.0 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC} + Win32Proj + StaticLib1 + 10.0.16299.0 + + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);..\zlib;..\libsparse\include;..\bzip2 + + + true + $(VC_IncludePath);$(WindowsSDK_IncludePath);..\zlib;..\libsparse\include;..\bzip2 + + + false + $(Configuration)\$(ProjectName)\ + $(SolutionDir)$(Platform)\$(Configuration)\ + $(VC_IncludePath);$(WindowsSDK_IncludePath);..\zlib;..\libsparse\include;..\bzip2 + + + false + $(VC_IncludePath);$(WindowsSDK_IncludePath);..\zlib;..\libsparse\include;..\bzip2 + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + ..\libusb\libusb + + + Windows + true + + + createversion.bat ..\libuuu + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + ..\libusb\libusb + + + Windows + true + + + createversion.bat ..\libuuu + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + ..\libusb\libusb + MultiThreaded + + + Windows + true + true + true + + + createversion.bat ..\libuuu + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + true + ..\libusb\libusb + MultiThreaded + + + Windows + true + true + true + + + createversion.bat ..\libuuu + + + + + + \ No newline at end of file diff --git a/msvc/libuuu.vcxproj.filters b/msvc/libuuu.vcxproj.filters new file mode 100644 index 0000000..9970701 --- /dev/null +++ b/msvc/libuuu.vcxproj.filters @@ -0,0 +1,123 @@ + + + + + {56b8d729-e8e8-4b90-be62-ef7a1d4e77c2} + + + {9880b066-0013-4325-97c5-de85ce13b31f} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/msvc/uuu-static-link.sln b/msvc/uuu-static-link.sln new file mode 100644 index 0000000..b014849 --- /dev/null +++ b/msvc/uuu-static-link.sln @@ -0,0 +1,77 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libuuu", "libuuu.vcxproj", "{048A9BD1-EEF8-4119-9DEF-219D7E177AAC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib.vcxproj", "{59666402-A775-49AC-A9BD-19FD528F5FD9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bzip2", "bzip2.vcxproj", "{B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (static)", "..\libusb\msvc\libusb_static_2017.vcxproj", "{349EE8F9-7D25-4909-AAF5-FF3FADE72187}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uuu-static-link", "uuu-static-link.vcxproj", "{2950F8EE-7895-4431-858A-C21EE105C242}" + ProjectSection(ProjectDependencies) = postProject + {59666402-A775-49AC-A9BD-19FD528F5FD9} = {59666402-A775-49AC-A9BD-19FD528F5FD9} + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF} = {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF} + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC} = {048A9BD1-EEF8-4119-9DEF-219D7E177AAC} + {349EE8F9-7D25-4909-AAF5-FF3FADE72187} = {349EE8F9-7D25-4909-AAF5-FF3FADE72187} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Debug|x64.ActiveCfg = Debug|x64 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Debug|x64.Build.0 = Debug|x64 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Debug|x86.ActiveCfg = Debug|Win32 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Debug|x86.Build.0 = Debug|Win32 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Release|x64.ActiveCfg = Release|x64 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Release|x64.Build.0 = Release|x64 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Release|x86.ActiveCfg = Release|Win32 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Release|x86.Build.0 = Release|Win32 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Debug|x64.ActiveCfg = Debug|x64 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Debug|x64.Build.0 = Debug|x64 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Debug|x86.ActiveCfg = Debug|Win32 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Debug|x86.Build.0 = Debug|Win32 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Release|x64.ActiveCfg = Release|x64 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Release|x64.Build.0 = Release|x64 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Release|x86.ActiveCfg = Release|Win32 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Release|x86.Build.0 = Release|Win32 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Debug|x64.ActiveCfg = Debug|x64 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Debug|x64.Build.0 = Debug|x64 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Debug|x86.ActiveCfg = Debug|Win32 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Debug|x86.Build.0 = Debug|Win32 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Release|x64.ActiveCfg = Release|x64 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Release|x64.Build.0 = Release|x64 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Release|x86.ActiveCfg = Release|Win32 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Release|x86.Build.0 = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x86.ActiveCfg = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x86.Build.0 = Debug|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x86.ActiveCfg = Release|Win32 + {349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x86.Build.0 = Release|Win32 + {2950F8EE-7895-4431-858A-C21EE105C242}.Debug|x64.ActiveCfg = Debug|x64 + {2950F8EE-7895-4431-858A-C21EE105C242}.Debug|x64.Build.0 = Debug|x64 + {2950F8EE-7895-4431-858A-C21EE105C242}.Debug|x86.ActiveCfg = Debug|Win32 + {2950F8EE-7895-4431-858A-C21EE105C242}.Debug|x86.Build.0 = Debug|Win32 + {2950F8EE-7895-4431-858A-C21EE105C242}.Release|x64.ActiveCfg = Release|x64 + {2950F8EE-7895-4431-858A-C21EE105C242}.Release|x64.Build.0 = Release|x64 + {2950F8EE-7895-4431-858A-C21EE105C242}.Release|x86.ActiveCfg = Release|Win32 + {2950F8EE-7895-4431-858A-C21EE105C242}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {93EA02D1-E396-4390-B160-58909BBB9556} + EndGlobalSection +EndGlobal diff --git a/msvc/uuu-static-link.vcxproj b/msvc/uuu-static-link.vcxproj new file mode 100644 index 0000000..142c072 --- /dev/null +++ b/msvc/uuu-static-link.vcxproj @@ -0,0 +1,312 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + 15.0 + {2950F8EE-7895-4431-858A-C21EE105C242} + Win32Proj + uuu + 10.0.16299.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + $(Platform)\$(Configuration) + Build + uuu + + + true + Build + $(Platform)\$(Configuration) + uuu + + + false + $(Configuration)\$(ProjectName)\ + $(Platform)\$(Configuration) + Build + uuu + + + false + Build + $(Platform)\$(Configuration) + uuu + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + ../libusb/$(Platform)/$(Configuration)/lib/libusb-1.0.lib;$(Platform)/$(Configuration)/libuuu.lib;$(Platform)/$(Configuration)/zlib.lib;%(AdditionalDependencies);$(Platform)/$(Configuration)/bzip2.lib + + + echo R^"####( > $(SolutionDir)\..\uuu\uuu.clst +type $(SolutionDir)\..\uuu\uuu.lst >> $(SolutionDir)\..\uuu\uuu.clst +echo )####^" >> $(SolutionDir)\..\uuu\uuu.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_loader.clst +type $(SolutionDir)\..\uuu\emmc_burn_loader.lst >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\qspi_burn_loader.clst +type $(SolutionDir)\..\uuu\qspi_burn_loader.lst >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_loader.clst +type $(SolutionDir)\..\uuu\sd_burn_loader.lst >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\spl_boot.clst +type $(SolutionDir)\..\uuu\spl_boot.lst >> $(SolutionDir)\..\uuu\spl_boot.clst +echo )####^" >> $(SolutionDir)\..\uuu\spl_boot.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_all.clst +type $(SolutionDir)\..\uuu\emmc_burn_all.lst >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_all.clst +type $(SolutionDir)\..\uuu\sd_burn_all.lst >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\fat_write.clst +type $(SolutionDir)\..\uuu\fat_write.lst >> $(SolutionDir)\..\uuu\fat_write.clst +echo )####^" >> $(SolutionDir)\..\uuu\fat_write.clst +echo R^"####( > $(SolutionDir)\..\uuu\nand_burn_loader.clst +type $(SolutionDir)\..\uuu\nand_burn_loader.lst >> $(SolutionDir)\..\uuu\nand_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\nand_burn_loader.clst + + + + true + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + ../libusb/$(Platform)/$(Configuration)/lib/libusb-1.0.lib;$(Platform)/$(Configuration)/libuuu.lib;$(Platform)/$(Configuration)/zlib.lib;%(AdditionalDependencies);$(Platform)/$(Configuration)/bzip2.lib + + + echo R^"####( > $(SolutionDir)\..\uuu\uuu.clst +type $(SolutionDir)\..\uuu\uuu.lst >> $(SolutionDir)\..\uuu\uuu.clst +echo )####^" >> $(SolutionDir)\..\uuu\uuu.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_loader.clst +type $(SolutionDir)\..\uuu\emmc_burn_loader.lst >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\qspi_burn_loader.clst +type $(SolutionDir)\..\uuu\qspi_burn_loader.lst >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_loader.clst +type $(SolutionDir)\..\uuu\sd_burn_loader.lst >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\spl_boot.clst +type $(SolutionDir)\..\uuu\spl_boot.lst >> $(SolutionDir)\..\uuu\spl_boot.clst +echo )####^" >> $(SolutionDir)\..\uuu\spl_boot.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_all.clst +type $(SolutionDir)\..\uuu\emmc_burn_all.lst >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_all.clst +type $(SolutionDir)\..\uuu\sd_burn_all.lst >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\fat_write.clst +type $(SolutionDir)\..\uuu\fat_write.lst >> $(SolutionDir)\..\uuu\fat_write.clst +echo )####^" >> $(SolutionDir)\..\uuu\fat_write.clst +echo R^"####( > $(SolutionDir)\..\uuu\nand_burn_loader.clst +type $(SolutionDir)\..\uuu\nand_burn_loader.lst >> $(SolutionDir)\..\uuu\nand_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\nand_burn_loader.clst + + + + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + ../libusb/$(Platform)/$(Configuration)/lib/libusb-1.0.lib;$(Platform)/$(Configuration)/libuuu.lib;$(Platform)/$(Configuration)/zlib.lib;%(AdditionalDependencies);$(Platform)/$(Configuration)/bzip2.lib + + + echo R^"####( > $(SolutionDir)\..\uuu\uuu.clst +type $(SolutionDir)\..\uuu\uuu.lst >> $(SolutionDir)\..\uuu\uuu.clst +echo )####^" >> $(SolutionDir)\..\uuu\uuu.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_loader.clst +type $(SolutionDir)\..\uuu\emmc_burn_loader.lst >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\qspi_burn_loader.clst +type $(SolutionDir)\..\uuu\qspi_burn_loader.lst >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_loader.clst +type $(SolutionDir)\..\uuu\sd_burn_loader.lst >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\spl_boot.clst +type $(SolutionDir)\..\uuu\spl_boot.lst >> $(SolutionDir)\..\uuu\spl_boot.clst +echo )####^" >> $(SolutionDir)\..\uuu\spl_boot.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_all.clst +type $(SolutionDir)\..\uuu\emmc_burn_all.lst >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_all.clst +type $(SolutionDir)\..\uuu\sd_burn_all.lst >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\fat_write.clst +type $(SolutionDir)\..\uuu\fat_write.lst >> $(SolutionDir)\..\uuu\fat_write.clst +echo )####^" >> $(SolutionDir)\..\uuu\fat_write.clst +echo R^"####( > $(SolutionDir)\..\uuu\nand_burn_loader.clst +type $(SolutionDir)\..\uuu\nand_burn_loader.lst >> $(SolutionDir)\..\uuu\nand_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\nand_burn_loader.clst + + + + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + ../libusb/$(Platform)/$(Configuration)/lib/libusb-1.0.lib;$(Platform)/$(Configuration)/libuuu.lib;$(Platform)/$(Configuration)/zlib.lib;%(AdditionalDependencies);$(Platform)/$(Configuration)/bzip2.lib + + + echo R^"####( > $(SolutionDir)\..\uuu\uuu.clst +type $(SolutionDir)\..\uuu\uuu.lst >> $(SolutionDir)\..\uuu\uuu.clst +echo )####^" >> $(SolutionDir)\..\uuu\uuu.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_loader.clst +type $(SolutionDir)\..\uuu\emmc_burn_loader.lst >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\qspi_burn_loader.clst +type $(SolutionDir)\..\uuu\qspi_burn_loader.lst >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_loader.clst +type $(SolutionDir)\..\uuu\sd_burn_loader.lst >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\spl_boot.clst +type $(SolutionDir)\..\uuu\spl_boot.lst >> $(SolutionDir)\..\uuu\spl_boot.clst +echo )####^" >> $(SolutionDir)\..\uuu\spl_boot.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_all.clst +type $(SolutionDir)\..\uuu\emmc_burn_all.lst >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_all.clst +type $(SolutionDir)\..\uuu\sd_burn_all.lst >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\fat_write.clst +type $(SolutionDir)\..\uuu\fat_write.lst >> $(SolutionDir)\..\uuu\fat_write.clst +echo )####^" >> $(SolutionDir)\..\uuu\fat_write.clst +echo R^"####( > $(SolutionDir)\..\uuu\nand_burn_loader.clst +type $(SolutionDir)\..\uuu\nand_burn_loader.lst >> $(SolutionDir)\..\uuu\nand_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\nand_burn_loader.clst + + + + true + + + + + + \ No newline at end of file diff --git a/msvc/uuu.sln b/msvc/uuu.sln new file mode 100644 index 0000000..544dcdb --- /dev/null +++ b/msvc/uuu.sln @@ -0,0 +1,77 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uuu", "uuu.vcxproj", "{2950F8EE-7895-4431-858A-C21EE105C242}" + ProjectSection(ProjectDependencies) = postProject + {59666402-A775-49AC-A9BD-19FD528F5FD9} = {59666402-A775-49AC-A9BD-19FD528F5FD9} + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF} = {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF} + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC} = {048A9BD1-EEF8-4119-9DEF-219D7E177AAC} + {349EE8FA-7D25-4909-AAF5-FF3FADE72187} = {349EE8FA-7D25-4909-AAF5-FF3FADE72187} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libuuu", "libuuu.vcxproj", "{048A9BD1-EEF8-4119-9DEF-219D7E177AAC}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib.vcxproj", "{59666402-A775-49AC-A9BD-19FD528F5FD9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libusb-1.0 (dll)", "..\libusb\msvc\libusb_dll_2017.vcxproj", "{349EE8FA-7D25-4909-AAF5-FF3FADE72187}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bzip2", "bzip2.vcxproj", "{B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2950F8EE-7895-4431-858A-C21EE105C242}.Debug|x64.ActiveCfg = Debug|x64 + {2950F8EE-7895-4431-858A-C21EE105C242}.Debug|x64.Build.0 = Debug|x64 + {2950F8EE-7895-4431-858A-C21EE105C242}.Debug|x86.ActiveCfg = Debug|Win32 + {2950F8EE-7895-4431-858A-C21EE105C242}.Debug|x86.Build.0 = Debug|Win32 + {2950F8EE-7895-4431-858A-C21EE105C242}.Release|x64.ActiveCfg = Release|x64 + {2950F8EE-7895-4431-858A-C21EE105C242}.Release|x64.Build.0 = Release|x64 + {2950F8EE-7895-4431-858A-C21EE105C242}.Release|x86.ActiveCfg = Release|Win32 + {2950F8EE-7895-4431-858A-C21EE105C242}.Release|x86.Build.0 = Release|Win32 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Debug|x64.ActiveCfg = Debug|x64 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Debug|x64.Build.0 = Debug|x64 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Debug|x86.ActiveCfg = Debug|Win32 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Debug|x86.Build.0 = Debug|Win32 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Release|x64.ActiveCfg = Release|x64 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Release|x64.Build.0 = Release|x64 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Release|x86.ActiveCfg = Release|Win32 + {048A9BD1-EEF8-4119-9DEF-219D7E177AAC}.Release|x86.Build.0 = Release|Win32 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Debug|x64.ActiveCfg = Debug|x64 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Debug|x64.Build.0 = Debug|x64 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Debug|x86.ActiveCfg = Debug|Win32 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Debug|x86.Build.0 = Debug|Win32 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Release|x64.ActiveCfg = Release|x64 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Release|x64.Build.0 = Release|x64 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Release|x86.ActiveCfg = Release|Win32 + {59666402-A775-49AC-A9BD-19FD528F5FD9}.Release|x86.Build.0 = Release|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|x86.ActiveCfg = Debug|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Debug|x86.Build.0 = Debug|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|x64.ActiveCfg = Release|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|x86.ActiveCfg = Release|Win32 + {349EE8FA-7D25-4909-AAF5-FF3FADE72187}.Release|x86.Build.0 = Release|Win32 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Debug|x64.ActiveCfg = Debug|x64 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Debug|x64.Build.0 = Debug|x64 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Debug|x86.ActiveCfg = Debug|Win32 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Debug|x86.Build.0 = Debug|Win32 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Release|x64.ActiveCfg = Release|x64 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Release|x64.Build.0 = Release|x64 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Release|x86.ActiveCfg = Release|Win32 + {B54C35CD-5FFE-4FEA-90BA-32894BCFC6BF}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {93EA02D1-E396-4390-B160-58909BBB9556} + EndGlobalSection +EndGlobal diff --git a/msvc/uuu.vcxproj b/msvc/uuu.vcxproj new file mode 100644 index 0000000..ed0280b --- /dev/null +++ b/msvc/uuu.vcxproj @@ -0,0 +1,316 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + 15.0 + {2950F8EE-7895-4431-858A-C21EE105C242} + Win32Proj + uuu + 10.0.16299.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + true + $(Configuration)\$(ProjectName)\ + $(Platform)\$(Configuration) + Build + + + true + Build + $(Platform)\$(Configuration) + + + false + $(Configuration)\$(ProjectName)\ + $(Platform)\$(Configuration) + Build + + + false + Build + $(Platform)\$(Configuration) + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + ../libusb/$(Platform)/$(Configuration)/dll/libusb-1.0.lib;$(Platform)/$(Configuration)/libuuu.lib;$(Platform)/$(Configuration)/zlib.lib;%(AdditionalDependencies);$(Platform)/$(Configuration)/bzip2.lib + + + echo R^"####( > $(SolutionDir)\..\uuu\uuu.clst +type $(SolutionDir)\..\uuu\uuu.lst >> $(SolutionDir)\..\uuu\uuu.clst +echo )####^" >> $(SolutionDir)\..\uuu\uuu.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_loader.clst +type $(SolutionDir)\..\uuu\emmc_burn_loader.lst >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\qspi_burn_loader.clst +type $(SolutionDir)\..\uuu\qspi_burn_loader.lst >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_loader.clst +type $(SolutionDir)\..\uuu\sd_burn_loader.lst >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\spl_boot.clst +type $(SolutionDir)\..\uuu\spl_boot.lst >> $(SolutionDir)\..\uuu\spl_boot.clst +echo )####^" >> $(SolutionDir)\..\uuu\spl_boot.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_all.clst +type $(SolutionDir)\..\uuu\emmc_burn_all.lst >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_all.clst +type $(SolutionDir)\..\uuu\sd_burn_all.lst >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\fat_write.clst +type $(SolutionDir)\..\uuu\fat_write.lst >> $(SolutionDir)\..\uuu\fat_write.clst +echo )####^" >> $(SolutionDir)\..\uuu\fat_write.clst +echo R^"####( > $(SolutionDir)\..\uuu\nand_burn_loader.clst +type $(SolutionDir)\..\uuu\nand_burn_loader.lst >> $(SolutionDir)\..\uuu\nand_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\nand_burn_loader.clst + + + true + + + copy /y "$(SolutionDir)..\libusb\$(Platform)\$(Configuration)\dll\*.dll" $(Platform)\$(Configuration)\ + + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + ../libusb/$(Platform)/$(Configuration)/dll/libusb-1.0.lib;$(Platform)/$(Configuration)/libuuu.lib;$(Platform)/$(Configuration)/zlib.lib;%(AdditionalDependencies);$(Platform)/$(Configuration)/bzip2.lib + + + echo R^"####( > $(SolutionDir)\..\uuu\uuu.clst +type $(SolutionDir)\..\uuu\uuu.lst >> $(SolutionDir)\..\uuu\uuu.clst +echo )####^" >> $(SolutionDir)\..\uuu\uuu.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_loader.clst +type $(SolutionDir)\..\uuu\emmc_burn_loader.lst >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\qspi_burn_loader.clst +type $(SolutionDir)\..\uuu\qspi_burn_loader.lst >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_loader.clst +type $(SolutionDir)\..\uuu\sd_burn_loader.lst >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\spl_boot.clst +type $(SolutionDir)\..\uuu\spl_boot.lst >> $(SolutionDir)\..\uuu\spl_boot.clst +echo )####^" >> $(SolutionDir)\..\uuu\spl_boot.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_all.clst +type $(SolutionDir)\..\uuu\emmc_burn_all.lst >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_all.clst +type $(SolutionDir)\..\uuu\sd_burn_all.lst >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\fat_write.clst +type $(SolutionDir)\..\uuu\fat_write.lst >> $(SolutionDir)\..\uuu\fat_write.clst +echo )####^" >> $(SolutionDir)\..\uuu\fat_write.clst +echo R^"####( > $(SolutionDir)\..\uuu\nand_burn_loader.clst +type $(SolutionDir)\..\uuu\nand_burn_loader.lst >> $(SolutionDir)\..\uuu\nand_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\nand_burn_loader.clst + + + true + + + copy /y "$(SolutionDir)..\libusb\$(Platform)\$(Configuration)\dll\*.dll" $(Platform)\$(Configuration)\ + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + ../libusb/$(Platform)/$(Configuration)/dll/libusb-1.0.lib;$(Platform)/$(Configuration)/libuuu.lib;$(Platform)/$(Configuration)/zlib.lib;%(AdditionalDependencies);$(Platform)/$(Configuration)/bzip2.lib + + + echo R^"####( > $(SolutionDir)\..\uuu\uuu.clst +type $(SolutionDir)\..\uuu\uuu.lst >> $(SolutionDir)\..\uuu\uuu.clst +echo )####^" >> $(SolutionDir)\..\uuu\uuu.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_loader.clst +type $(SolutionDir)\..\uuu\emmc_burn_loader.lst >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\qspi_burn_loader.clst +type $(SolutionDir)\..\uuu\qspi_burn_loader.lst >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_loader.clst +type $(SolutionDir)\..\uuu\sd_burn_loader.lst >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\spl_boot.clst +type $(SolutionDir)\..\uuu\spl_boot.lst >> $(SolutionDir)\..\uuu\spl_boot.clst +echo )####^" >> $(SolutionDir)\..\uuu\spl_boot.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_all.clst +type $(SolutionDir)\..\uuu\emmc_burn_all.lst >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_all.clst +type $(SolutionDir)\..\uuu\sd_burn_all.lst >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\fat_write.clst +type $(SolutionDir)\..\uuu\fat_write.lst >> $(SolutionDir)\..\uuu\fat_write.clst +echo )####^" >> $(SolutionDir)\..\uuu\fat_write.clst +echo R^"####( > $(SolutionDir)\..\uuu\nand_burn_loader.clst +type $(SolutionDir)\..\uuu\nand_burn_loader.lst >> $(SolutionDir)\..\uuu\nand_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\nand_burn_loader.clst + + + true + + + copy /y "$(SolutionDir)..\libusb\$(Platform)\$(Configuration)\dll\*.dll" $(Platform)\$(Configuration)\ + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + + + Console + true + true + true + ../libusb/$(Platform)/$(Configuration)/dll/libusb-1.0.lib;$(Platform)/$(Configuration)/libuuu.lib;$(Platform)/$(Configuration)/zlib.lib;%(AdditionalDependencies);$(Platform)/$(Configuration)/bzip2.lib + + + echo R^"####( > $(SolutionDir)\..\uuu\uuu.clst +type $(SolutionDir)\..\uuu\uuu.lst >> $(SolutionDir)\..\uuu\uuu.clst +echo )####^" >> $(SolutionDir)\..\uuu\uuu.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_loader.clst +type $(SolutionDir)\..\uuu\emmc_burn_loader.lst >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\qspi_burn_loader.clst +type $(SolutionDir)\..\uuu\qspi_burn_loader.lst >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\qspi_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_loader.clst +type $(SolutionDir)\..\uuu\sd_burn_loader.lst >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_loader.clst +echo R^"####( > $(SolutionDir)\..\uuu\spl_boot.clst +type $(SolutionDir)\..\uuu\spl_boot.lst >> $(SolutionDir)\..\uuu\spl_boot.clst +echo )####^" >> $(SolutionDir)\..\uuu\spl_boot.clst +echo R^"####( > $(SolutionDir)\..\uuu\emmc_burn_all.clst +type $(SolutionDir)\..\uuu\emmc_burn_all.lst >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\emmc_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\sd_burn_all.clst +type $(SolutionDir)\..\uuu\sd_burn_all.lst >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo )####^" >> $(SolutionDir)\..\uuu\sd_burn_all.clst +echo R^"####( > $(SolutionDir)\..\uuu\fat_write.clst +type $(SolutionDir)\..\uuu\fat_write.lst >> $(SolutionDir)\..\uuu\fat_write.clst +echo )####^" >> $(SolutionDir)\..\uuu\fat_write.clst +echo R^"####( > $(SolutionDir)\..\uuu\nand_burn_loader.clst +type $(SolutionDir)\..\uuu\nand_burn_loader.lst >> $(SolutionDir)\..\uuu\nand_burn_loader.clst +echo )####^" >> $(SolutionDir)\..\uuu\nand_burn_loader.clst + + + true + + + copy /y "$(SolutionDir)..\libusb\$(Platform)\$(Configuration)\dll\*.dll" $(Platform)\$(Configuration)\ + + + + + + \ No newline at end of file diff --git a/msvc/uuu.vcxproj.filters b/msvc/uuu.vcxproj.filters new file mode 100644 index 0000000..1aee593 --- /dev/null +++ b/msvc/uuu.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/msvc/zlib.vcxproj b/msvc/zlib.vcxproj new file mode 100644 index 0000000..0decf32 --- /dev/null +++ b/msvc/zlib.vcxproj @@ -0,0 +1,193 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {59666402-A775-49AC-A9BD-19FD528F5FD9} + Win32Proj + zlib + 10.0.16299.0 + + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + true + + + true + $(SolutionDir)$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)$(Platform)\$(Configuration)\ + + + false + + + + NotUsing + Level3 + Disabled + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + false + + + Windows + true + + + true + + + + + NotUsing + Level3 + Disabled + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + false + + + Windows + true + + + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + false + MultiThreaded + + + Windows + true + true + true + + + true + + + + + NotUsing + Level3 + MaxSpeed + true + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + true + false + MultiThreaded + + + Windows + true + true + true + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/msvc/zlib.vcxproj.filters b/msvc/zlib.vcxproj.filters new file mode 100644 index 0000000..fe87903 --- /dev/null +++ b/msvc/zlib.vcxproj.filters @@ -0,0 +1,72 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/snap/gui/universal-update-utility.png b/snap/gui/universal-update-utility.png new file mode 100644 index 0000000000000000000000000000000000000000..40ebbbc8c680ba0ac26671a91fc190e7273afb8e GIT binary patch literal 6540 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 + exit 1 + fi +else + snapctl set disable-snap-confinement-warnings=false +fi diff --git a/snap/hooks/post-refresh b/snap/hooks/post-refresh new file mode 100644 index 0000000..64963d3 --- /dev/null +++ b/snap/hooks/post-refresh @@ -0,0 +1,7 @@ +#!/bin/sh -e + +echo "test" > /etc/udev/rules.d/test +uuu -udev >> /etc/udev/rules.d/70-uuu.rules +udevadm control --reload + + diff --git a/snap/local/LICENSE b/snap/local/LICENSE new file mode 100644 index 0000000..81802d6 --- /dev/null +++ b/snap/local/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Snapcrafters + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/snap/local/bash-completion/universal-update-utility b/snap/local/bash-completion/universal-update-utility new file mode 100644 index 0000000..584b9e2 --- /dev/null +++ b/snap/local/bash-completion/universal-update-utility @@ -0,0 +1,5 @@ +_uuu_autocomplete() +{ + COMPREPLY=($(uuu $1 $2 $3)) +} +complete -o nospace -F _uuu_autocomplete uuu diff --git a/snap/local/launchers/universal-update-utility-launch b/snap/local/launchers/universal-update-utility-launch new file mode 100755 index 0000000..87ffb3f --- /dev/null +++ b/snap/local/launchers/universal-update-utility-launch @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# This is the maintainence launcher for the snap, make necessary runtime +# environment changes to make the snap work here. You may also insert +# security confinement/deprecation/obsoletion notice of the snap here. + +# lint: The use of backticks in the printf messages is intended +# shellcheck disable=SC2016 + +set \ + -o errexit \ + -o errtrace \ + -o nounset \ + -o pipefail + +#export IMPORTANT_ENVIRONMENT_VARIABLE=value + +# Run the command, if fails, check and present security confinement +# warnings to the user +if ! "${@}" \ + && test "$(snapctl get disable-snap-confinement-warnings)" != true; then + snap_confinement_warning_triggered=false + if ! snapctl is-connected home; then + snap_confinement_warning_triggered=true + printf \ + "Warning: It appears that the %s snap isn't connected to the \`home\` interface, accessing files under your home directory will not be possible.\n\n" \ + "${SNAP_NAME}" \ + 1>&2 + printf \ + "To fix this problem run the following command as root:\n\n snap connect %s:home\n\n" \ + "${SNAP_NAME}" \ + 1>&2 + fi + + if ! snapctl is-connected raw-usb; then + snap_confinement_warning_triggered=true + printf \ + "Warning: It appears that the %s snap isn't connected to the \`raw-usb\` interface, direct-access of USB devices will not be possible.\n\n" \ + "${SNAP_NAME}" \ + 1>&2 + printf \ + "To fix this problem run the following command as root:\n\n snap connect %s:raw-usb\n\n" \ + "${SNAP_NAME}" \ + 1>&2 + fi + + if ! snapctl is-connected removable-media; then + snap_confinement_warning_triggered=true + printf \ + "Warning: It appears that the %s snap isn't connected to the \`removable-media\` interface, accessing files under /media and /mnt directory will not be possible.\n\n" \ + "${SNAP_NAME}" \ + 1>&2 + printf \ + "To fix this problem run the following command as root:\n\n snap connect %s:removable-media\n\n" \ + "${SNAP_NAME}" \ + 1>&2 + fi + + if test "${snap_confinement_warning_triggered}" == true; then + printf \ + "To disable these warnings, run the following command as root:\n\n snap set %s disable-snap-confinement-warnings=true\n\n" \ + "${SNAP_NAME}" \ + 1>&2 + fi +fi diff --git a/snap/local/screenshots/carbon-help.png b/snap/local/screenshots/carbon-help.png new file mode 100644 index 0000000000000000000000000000000000000000..795930e809f967c5fb6426c0c0c430453c1b7505 GIT binary patch literal 78208 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.0.16) +pkg_check_modules(LIBZ REQUIRED zlib) +find_package(Threads) + +if (STATIC) +set(OPENSSL_USE_STATIC_LIBS TRUE) +endif() + +find_package(OpenSSL) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -O2") + +if (STATIC) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc") +endif() + +set(LSTS + uuu.lst + emmc_burn_loader.lst + emmc_burn_all.lst + fat_write.lst + qspi_burn_loader.lst + sd_burn_loader.lst + spl_boot.lst + sd_burn_all.lst + nand_burn_loader.lst +) + +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/libuuu ${LIBUSB_LIBRARY_DIRS} ${LIBZ_LIBRARY_DIRS}) + +set(CLIST_EXECUTABLE ${CMAKE_CURRENT_SOURCE_DIR}/gen_txt_include.sh) +set(generated_files_dir "${CMAKE_BINARY_DIR}/uuu/gen") + +function(preprocess_clst out_var) + set(result) + foreach(in_f ${ARGN}) + set(out_f "${generated_files_dir}/${in_f}") + string(REPLACE ".lst" ".clst" out_f ${out_f}) + add_custom_command(OUTPUT ${out_f} + PRE_BUILD + COMMAND mkdir -p ${generated_files_dir} + COMMAND ${CLIST_EXECUTABLE} ${in_f} ${out_f} + DEPENDS ${in_f} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Creating preprocessed clst file ${out_f}" + VERBATIM + ) + list(APPEND result ${out_f}) + endforeach() + set(${out_var} "${result}" PARENT_SCOPE) +endfunction() + +preprocess_clst(CLSTS ${LSTS}) + +include_directories(${generated_files_dir}) + +set(SOURCES + uuu.cpp + buildincmd.cpp + autocomplete.cpp + ${CLSTS} +) + +add_executable(uuu ${SOURCES}) +target_link_libraries(uuu uuc_s ${OPENSSL_LIBRARIES} ${LIBUSB_LIBRARIES} ${LIBZ_LIBRARIES} dl bz2) + +install(TARGETS uuu DESTINATION bin) +target_compile_definitions(uuu + PRIVATE "TARGET_PATH=\"${CMAKE_INSTALL_PREFIX}/bin/uuu\"" +) diff --git a/uuu/autocomplete.cpp b/uuu/autocomplete.cpp new file mode 100644 index 0000000..9ca4973 --- /dev/null +++ b/uuu/autocomplete.cpp @@ -0,0 +1,230 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "buildincmd.h" + +#include "../libuuu/libuuu.h" + +#ifndef _MSC_VER +#include +#include +#else +#include +#endif + +using namespace std; + +void linux_auto_arg(const char *space = " ", const char * filter = "") +{ + string str = filter; + + const char *param[] = { "-b", "-d", "-v", "-V", "-s", NULL }; + int i = 0; + + for (int i = 0; param[i]; i++) + { + if (str.find(param[i]) == string::npos) + cout << param[i] << space << endl; + } +} + +int linux_autocomplete_ls(const char *path, void *p) +{ + cout << path + 2 << endl; + return 0; +} + +void linux_autocomplete(int argc, char **argv) +{ + string last; + string cur; + + if (argc == 3) + { + last = argv[2]; + }else + { + cur = argv[2]; + last = argv[3]; + } + + if (cur[0] == '-') + { + if (cur.size() == 1) + linux_auto_arg(); + else + cout << cur << " " << endl; + + return; + } + + if (last.size()>=3) + { + if (last.substr(last.size() - 3) == "uuu" &&(cur.empty() || cur[0] == '-')) + { + linux_auto_arg(); + cout << cur << endl; + } + + }else if(last.empty()) + { + linux_auto_arg(); + } + else if (last == "-b") + { + return g_BuildScripts.PrintAutoComplete(cur); + + }else if(last[0] == '-') + { + linux_auto_arg(); + } + + uuu_for_each_ls_file(linux_autocomplete_ls, cur.c_str(), NULL); +} + +string get_next_word(string str, size_t &pos) +{ + size_t start = 0; + start = str.find(' ', pos); + string sub = str.substr(pos, start - pos); + pos = start == string::npos ? start : start + 1; + return sub; +} + +void power_shell_autocomplete(const char *p) +{ + string pstr = p; + size_t pos = 0; + + string file; + + vector argv; string params; + while (pos != string::npos && pos < pstr.size()) + { + file = get_next_word(pstr, pos); + argv.push_back(file); + + if (file.size() && file[0] == '-') + params += " " + file; + } + + string last = argv[argv.size() - 1]; + string prev = argv.size() > 1 ? argv[argv.size() - 2] : ""; + if (last == "-b" || prev == "-b") + { + string cur; + if (prev == "-b") + cur = last; + + if (g_BuildScripts.find(cur) == g_BuildScripts.end()) + g_BuildScripts.PrintAutoComplete(cur, ""); + + last.clear(); + } + else + { + if (last[0] == '-' || argv.size() == 1) + linux_auto_arg("", params.c_str()); + } + + if (argv.size() == 1) + last.clear(); + + uuu_for_each_ls_file(linux_autocomplete_ls, last.c_str(), NULL); +} + +int auto_complete(int argc, char**argv) +{ + if (argc == 4 || argc == 3) + { + string str = argv[1]; + if (str.size() >= 3) + if (str.substr(str.size() - 3) == "uuu") + { + linux_autocomplete(argc, argv); + return 0; + } + } + + if (argc >= 2) + { + string str = argv[1]; + if (str == "-autocomplete") + { + power_shell_autocomplete(argc == 2 ? "" : argv[2]); + return 0; + } + } + + return 1; +} + +void print_autocomplete_help() +{ + +#ifndef _MSC_VER + { + cout << "Enjoy auto [tab] command complete by put below script into /etc/bash_completion.d/uuu" << endl; + cout << g_vt_kcyn; + cout << " _uuu_autocomplete()" < +#include +#include + +static std::string replace_str(std::string str, std::string key, std::string replace); +static std::string str_to_upper(const std::string &str); + +/** + * @brief Parse characters between argument name and its description and check + * if its an optional one + * @param[in] option The characters between argument name and its description to + * be parsed + * @return `0` in any case + */ +void BuiltInScript::Arg::parser(const std::string &option) +{ + const auto pos = option.find('['); + if (pos == std::string::npos) + { + return; + } + m_fallback_option = option.substr(pos + 1, option.find(']') - pos - 1); + m_flags = ARG_OPTION | ARG_OPTION_KEY; +} + +/** + * @brief Create a new BuiltInScript instance by extracting information from a + * BuiltInScriptRawData instance + * @param[in] p The BuiltInScriptRawData containing all data of the script this + * BuiltInScript instance shall represent + */ +BuiltInScript::BuiltInScript(const BuiltInScriptRawData * const p) : + m_text{p->m_text}, + m_desc{p->m_desc ? p->m_desc : ""}, + m_name{p->m_name ? p->m_name : ""} +{ + // Regular expression to detect script argument name occurrences + static const std::regex arg_name_regexp{R"####((@| )(_\S+))####"}; + + for (std::sregex_iterator it + = std::sregex_iterator{m_text.cbegin(), m_text.cend(), arg_name_regexp}; + it != std::sregex_iterator{}; ++it) + { + const std::string param{it->str(2)}; + if (!find_args(param)) + { + Arg a; + a.m_name = param; + a.m_flags = Arg::ARG_MUST; + m_args.emplace_back(std::move(a)); + } + } + + for (size_t i = 0; i < m_args.size(); i++) + { + std::string str; + str += "@"; + str += m_args[i].m_name; + const auto pos = m_text.find(str); + if (pos != std::string::npos) { + const auto start_descript = m_text.find('|', pos); + if (start_descript != std::string::npos) + { + m_args[i].m_desc = m_text.substr(start_descript + 1, + m_text.find('\n', start_descript) - start_descript - 1); + const std::string def{m_text.substr(pos, start_descript - pos)}; + m_args[i].parser(def); + } + } + } +} + +/** + * @brief Check if the BuiltInScript instance has an argument called `arg` + * @param[in] arg The argument for which its existence in the BuiltInScript + * shall be checked + * @return `true` if BuiltInScript has an argument named `arg`, `false` + * otherwise + */ +bool BuiltInScript::find_args(const std::string &arg) const +{ + return std::any_of(m_args.cbegin(), m_args.cend(), + [&arg](const Arg &brg){ return brg.m_name == arg; }); +} + +/** + * @brief Replace built-in script's arguments by actual values given in `args` + * @param[in] args The actual values that shall replace the arguments (the order + * must fit the order of the arguments in the script) + * @return A copy of the built-in script with the arguments replaced by their + * actual values + */ +std::string BuiltInScript::replace_script_args(const std::vector &args) const +{ + std::string script = m_text; + for (size_t i = 0; i < args.size() && i < m_args.size(); i++) + { + script = replace_str(script, m_args[i].m_name, args[i]); + } + + //handle option args; + for (size_t i = args.size(); i < m_args.size(); i++) + { + if (m_args[i].m_flags & Arg::ARG_OPTION_KEY) + { + for (size_t j = 0; j < args.size(); j++) + { + if (m_args[j].m_name == m_args[i].m_fallback_option) + { + script = replace_str(script, m_args[i].m_name, args[j]); + break; + } + } + } + } + return script; +} + +/** + * @brief Print the built-in script to `stdout` followed by a newline + */ +void BuiltInScript::show() const +{ + printf("%s\n", m_text.c_str()); +} + +/** + * @brief Print the script's name, its description and its arguments to stdout + */ +void BuiltInScript::show_cmd() const +{ + printf("\t%s%s%s\t%s\n", g_vt_boldwhite, m_name.c_str(), g_vt_default, m_desc.c_str()); + for (auto i = 0u; i < m_args.size(); ++i) + { + std::string desc{m_args[i].m_name}; + if (m_args[i].m_flags & Arg::ARG_OPTION) + { + desc += g_vt_boldwhite; + desc += "[Optional]"; + desc += g_vt_default; + } + desc += " "; + desc += m_args[i].m_desc; + printf("\t\targ%u: %s\n", i, desc.c_str()); + } +} + +/** + * @brief Create a new map by parsing an array of BuiltInScriptRawData instances + * @param[in] p Pointer to the first element of a BuiltInScriptRawData array + */ +BuiltInScriptMap::BuiltInScriptMap(const BuiltInScriptRawData*p) +{ + while (p->m_name) + { + emplace(p->m_name, p); + ++p; + } +} + +/** + * @brief Auto-complete names of built-in scripts if they match `match` + * @param[in] match The string against which the scripts' names will be machted + * @param[in] space A separator character which shall be printed after the + * completed script name + */ +void BuiltInScriptMap::PrintAutoComplete(const std::string &match, const char *space) const +{ + for (const auto &script_pair : *this) + { + if(script_pair.first.substr(0, match.size()) == match) + { + printf("%s%s\n", script_pair.first.c_str(), space); + } + } +} + +/** + * @brief Print information about all contained scripts to stdout + */ +void BuiltInScriptMap::ShowAll() const +{ + for (const auto &script_pair : *this) + { + script_pair.second.show_cmd(); + } +} + +/** + * @brief Print the names of all contained scripts to the given stream + * @param[in] file The stream to which the names shall be printed + */ +void BuiltInScriptMap::ShowCmds(FILE * const file) const +{ + fprintf(file, "<"); + for (auto iCol = begin(); iCol != end(); ++iCol) + { + fprintf(file, "%s", iCol->first.c_str()); + + auto i = iCol; + i++; + if(i != end()) + { + fprintf(file, "|"); + } + } + fprintf(file, ">"); +} + +/** + * @brief Replace a `key` substring of a string `str` by a replacement `replace` + * @param[in] str The string of which a copy with the replacements shall be + * created + * @param[in] key The string which shall be replaced + * @param[in] replace The string that shall replace ocurrences of `key` + * @return A new string instance with the replacements conducted on it + */ +static std::string replace_str(std::string str, std::string key, std::string replace) +{ + if (replace.size() > 4) + { + if (replace[replace.size() - 1] == '\"') + { + if (str_to_upper(replace.substr(replace.size() - 5)) == ".BZ2\"") + { + replace = replace.substr(0, replace.size() - 1); + replace += "/*\""; + } + + }else if (str_to_upper(replace.substr(replace.size() - 4)) == ".BZ2") + { + replace += "/*"; + } + } + + for (size_t j = 0; (j = str.find(key, j)) != std::string::npos;) + { + if (j == 0 || (j!=0 && str[j - 1] == ' ')) + str.replace(j, key.size(), replace); + j += key.size(); + } + return str; +} + +/** + * @brief Returns a copy of `str` with all applicable characters converted to + * uppercase + * @param[in] str The string for which an uppercase copy shall be created + * @return The copy of `str` converted to uppercase + */ +static std::string str_to_upper(const std::string &str) +{ + const std::locale loc; + std::string s; + s.reserve(str.size()); + + for (size_t i = 0; i < str.size(); ++i) + { + s.push_back(std::toupper(str[i], loc)); + } + + return s; +} + +//! Array containing raw information about all the built-in scripts of uuu +static constexpr BuiltInScriptRawData g_builtin_cmd[] = +{ + { + "emmc", +#include "emmc_burn_loader.clst" + ,"burn boot loader to eMMC boot partition" + }, + { + "emmc_all", +#include "emmc_burn_all.clst" + ,"burn whole image to eMMC" + }, + { + "fat_write", +#include "fat_write.clst" + ,"update one file in fat partition, require uboot fastboot running in board" + }, + { + "nand", +#include "nand_burn_loader.clst" + ,"burn boot loader to NAND flash" + }, + { + "qspi", +#include "qspi_burn_loader.clst" + ,"burn boot loader to qspi nor flash" + }, + { + "sd", +#include "sd_burn_loader.clst" + ,"burn boot loader to sd card" + }, + { + "sd_all", +#include "sd_burn_all.clst" + ,"burn whole image to sd card" + }, + { + "spl", +#include "spl_boot.clst" + ,"boot spl and uboot" + }, + { + nullptr, + nullptr, + nullptr, + } +}; + +//! A map of the built-in scripts' names to their BuiltInScript representations +BuiltInScriptMap g_BuildScripts(g_builtin_cmd); diff --git a/uuu/buildincmd.h b/uuu/buildincmd.h new file mode 100644 index 0000000..9415117 --- /dev/null +++ b/uuu/buildincmd.h @@ -0,0 +1,124 @@ +/* +* Copyright 2018-2021 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#pragma once + +#include +#include +#include + +extern const char * g_vt_boldwhite; +extern const char * g_vt_default; +extern const char * g_vt_kcyn; +extern const char * g_vt_green; +extern const char * g_vt_red ; +extern const char * g_vt_yellow; + +/** + * @brief Structure to hold the raw data of a built-in script + */ +struct BuiltInScriptRawData +{ + //! The name of the built-in script + const char * const m_name = nullptr; + //! The actual built-in script itself + const char * const m_text = nullptr; + //! A description of the built-in script's purpose + const char * const m_desc = nullptr; +}; + +class BuiltInScript +{ +public: + /** + * @brief A class for representing arguments of built-in scripts represented + * by BuiltInScript + */ + class Arg + { + public: + enum + { + ARG_MUST = 0x1, + ARG_OPTION = 0x2, + ARG_OPTION_KEY = 0x4, + }; + + void parser(const std::string &option); + + //! The name of the argument + std::string m_name; + //! A description of the argument + std::string m_desc; + //! Flags of the argument (basically if it's optional or not) + uint32_t m_flags = ARG_MUST; + //! The argument whose value this one will fall back to if it's optional + //! and not given explicitly + std::string m_fallback_option; + }; + + BuiltInScript() {}; + BuiltInScript(const BuiltInScriptRawData*p); + + std::string replace_script_args(const std::vector &args) const; + void show() const; + void show_cmd() const; + + //! The actual script which is being represented + const std::string m_text; + //! A description of the script's purpose + const std::string m_desc; + //! A short name of the built-in script + const std::string m_name; + //! The arguments of the built-in script + std::vector m_args; + +private: + bool find_args(const std::string &arg) const; +}; + +/** + * @brief A map of all built-in scripts indexed by their names + * + * Each built-in script is represented by a BuiltInScript instance. + */ +class BuiltInScriptMap : public std::map +{ +public: + BuiltInScriptMap(const BuiltInScriptRawData*p); + + void PrintAutoComplete(const std::string &match, const char *space = " ") const; + void ShowAll() const; + void ShowCmds(FILE * file=stdout) const; +}; + +//! A map of the built-in scripts' names to their BuiltInScript representations +extern BuiltInScriptMap g_BuildScripts; diff --git a/uuu/emmc_burn_all.lst b/uuu/emmc_burn_all.lst new file mode 100644 index 0000000..d868d05 --- /dev/null +++ b/uuu/emmc_burn_all.lst @@ -0,0 +1,38 @@ +uuu_version 1.4.149 + +# @_flash.bin | bootloader, which can extract from wic image +# @_image [_flash.bin] | wic image burn to emmc. + + +# This command will be run when i.MX6/7 i.MX8MM, i.MX8MQ +SDP: boot -f _flash.bin -scanlimited 0x800000 + +# This command will be run when ROM support stream mode +# i.MX8QXP, i.MX8QM +SDPS: boot -scanterm -f _flash.bin -scanlimited 0x800000 + +# These commands will be run when use SPL and will be skipped if no spl +# SDPU will be deprecated. please use SDPV instead of SDPU +# { +SDPU: delay 1000 +SDPU: write -f _flash.bin -offset 0x57c00 +SDPU: jump -scanlimited 0x800000 +# } + +# These commands will be run when use SPL and will be skipped if no spl +# if (SPL support SDPV) +# { +SDPV: delay 1000 +SDPV: write -f _flash.bin -skipspl -scanterm -scanlimited 0x800000 +SDPV: jump -scanlimited 0x800000 +# } + + +FB: ucmd setenv fastboot_dev mmc +FB: ucmd setenv mmcdev ${emmc_dev} +FB: ucmd mmc dev ${emmc_dev} +FB: flash -raw2sparse all _image +FB: flash -scanterm -scanlimited 0x800000 bootloader _flash.bin +FB: ucmd if env exists emmc_ack; then ; else setenv emmc_ack 0; fi; +FB: ucmd mmc partconf ${emmc_dev} ${emmc_ack} 1 0 +FB: done diff --git a/uuu/emmc_burn_loader.lst b/uuu/emmc_burn_loader.lst new file mode 100644 index 0000000..27128fe --- /dev/null +++ b/uuu/emmc_burn_loader.lst @@ -0,0 +1,35 @@ +uuu_version 1.2.39 + +# @_flash.bin | bootloader +# @_image [_flash.bin] | image burn to emmc, default is the same as bootloader + +# This command will be run when i.MX6/7 i.MX8MM, i.MX8MQ +SDP: boot -f _flash.bin + +# This command will be run when ROM support stream mode +# i.MX8QXP, i.MX8QM +SDPS: boot -f _flash.bin + +# These commands will be run when use SPL and will be skipped if no spl +# SDPU will be deprecated. please use SDPV instead of SDPU +# { +SDPU: delay 1000 +SDPU: write -f _flash.bin -offset 0x57c00 +SDPU: jump +# } + +# These commands will be run when use SPL and will be skipped if no spl +# if (SPL support SDPV) +# { +SDPV: delay 1000 +SDPV: write -f _flash.bin -skipspl +SDPV: jump +# } + +FB: ucmd setenv fastboot_dev mmc +FB: ucmd setenv mmcdev ${emmc_dev} +FB: ucmd mmc dev ${emmc_dev} +FB: flash bootloader _image +FB: ucmd if env exists emmc_ack; then ; else setenv emmc_ack 0; fi; +FB: ucmd mmc partconf ${emmc_dev} ${emmc_ack} 1 0 +FB: Done diff --git a/uuu/fat_write.lst b/uuu/fat_write.lst new file mode 100644 index 0000000..5949aff --- /dev/null +++ b/uuu/fat_write.lst @@ -0,0 +1,12 @@ +uuu_version 1.1.4 + +# @_image | image, which cp to fat partition +# @_device | storage device, mmc\sata +# @_partition | fat partition number, like 1:1 +# @_filename [_image] | file name in target fat partition, only support rootdir now + +FB: ucmd setenv fastboot_buffer ${loadaddr} +FB: download -f _image +FB: ucmd if test ! -n "$fastboot_bytes"; then setenv fastboot_bytes $filesize; else true; fi +FB[-t 20000]: ucmd fatwrite _device _partition ${fastboot_buffer} _filename ${fastboot_bytes} +FB: done diff --git a/uuu/gen_txt_include.sh b/uuu/gen_txt_include.sh new file mode 100755 index 0000000..ea727c6 --- /dev/null +++ b/uuu/gen_txt_include.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +echo "R\"####(" > $2 +cat $1 >> $2 +echo ")####\"" >> $2 diff --git a/uuu/nand_burn_loader.lst b/uuu/nand_burn_loader.lst new file mode 100644 index 0000000..d81e3cc --- /dev/null +++ b/uuu/nand_burn_loader.lst @@ -0,0 +1,35 @@ +uuu_version 1.2.39 + +# @_flash.bin | bootloader +# @_image [_flash.bin] | image burn to nand, default is the same as bootloader + +# This command will be run when i.MX6/7 i.MX8MM, i.MX8MQ +SDP: boot -f _flash.bin + +# This command will be run when ROM support stream mode +# i.MX8QXP, i.MX8QM +SDPS: boot -f _flash.bin + +# These commands will be run when use SPL and will be skipped if no spl +# SDPU will be deprecated. please use SDPV instead of SDPU +# { +SDPU: delay 1000 +SDPU: write -f _flash.bin -offset 0x57c00 +SDPU: jump +# } + +# These commands will be run when use SPL and will be skipped if no spl +# if (SPL support SDPV) +# { +SDPV: delay 1000 +SDPV: write -f _flash.bin -skipspl +SDPV: jump +# } + +FB: ucmd setenv fastboot_buffer ${loadaddr} +FB: download -f _image +FB: ucmd if test ! -n "$fastboot_bytes"; then setenv fastboot_bytes $filesize; else true; fi +# Burn image to nandfit partition if needed +FB: ucmd if env exists nandfit_part; then nand erase.part nandfit; nand write ${fastboot_buffer} nandfit ${fastboot_bytes}; else true; fi; +FB: ucmd nandbcb init ${fastboot_buffer} nandboot ${fastboot_bytes} +FB: Done diff --git a/uuu/qspi_burn_loader.lst b/uuu/qspi_burn_loader.lst new file mode 100644 index 0000000..4028f18 --- /dev/null +++ b/uuu/qspi_burn_loader.lst @@ -0,0 +1,43 @@ +uuu_version 1.2.39 + +# @_flexspi.bin | bootloader +# @_image [_flexspi.bin] | image burn to flexspi, default is the same as bootloader + +# This command will be run when i.MX6/7 i.MX8MM, i.MX8MQ +SDP: boot -f _flexspi.bin + +# This command will be run when ROM support stream mode +# i.MX8QXP, i.MX8QM, skip QSPI header +SDPS: boot -f _flexspi.bin -skipfhdr + +# These commands will be run when use SPL and will be skipped if no spl +# SDPU will be deprecated. please use SDPV instead of SDPU +# { +SDPU: delay 1000 +SDPU: write -f _flexspi.bin -offset 0x10000 -skipfhdr +SDPU: jump +# } + +# These commands will be run when use SPL and will be skipped if no spl +# if (SPL support SDPV) +# { +SDPV: delay 1000 +SDPV: write -f _flexspi.bin -skipspl -skipfhdr +SDPV: jump +# } + +FB: ucmd setenv fastboot_buffer ${loadaddr} +FB: download -f _image + +FB: ucmd if test ! -n "$fastboot_bytes"; then setenv fastboot_bytes $filesize; else true; fi + +# Check Image if include flexspi header +FB: ucmd if qspihdr dump ${fastboot_buffer}; then setenv qspihdr_exist yes; else setenv qspihdr_exist no; fi; + +FB[-t 60000]: ucmd if test ${qspihdr_exist} = yes; then qspihdr init ${fastboot_buffer} ${fastboot_bytes} safe; else true; fi; + +#if uboot can't support qspihdr command, use uboot image to write qspi image, which require image include qspi flash header +FB: ucmd if test ${qspihdr_exist} = no; then sf probe; else true; fi; +FB[-t 40000]: ucmd if test ${qspihdr_exist} = no; then sf erase 0 +${fastboot_bytes}; else true; fi; +FB[-t 20000]: ucmd if test ${qspihdr_exist} = no; then sf write ${fastboot_buffer} 0 ${fastboot_bytes}; else true; fi; +FB: done diff --git a/uuu/sd_burn_all.lst b/uuu/sd_burn_all.lst new file mode 100644 index 0000000..ae6ec6d --- /dev/null +++ b/uuu/sd_burn_all.lst @@ -0,0 +1,35 @@ +uuu_version 1.4.149 + +# @_flash.bin | bootloader, which can extract from wic image +# @_image [_flash.bin] | wic image burn to emmc. + + +# This command will be run when i.MX6/7 i.MX8MM, i.MX8MQ +SDP: boot -f _flash.bin -scanlimited 0x800000 + +# This command will be run when ROM support stream mode +# i.MX8QXP, i.MX8QM +SDPS: boot -scanterm -f _flash.bin -scanlimited 0x800000 + +# These commands will be run when use SPL and will be skipped if no spl +# SDPU will be deprecated. please use SDPV instead of SDPU +# { +SDPU: delay 1000 +SDPU: write -f _flash.bin -offset 0x57c00 -scanlimited 0x800000 +SDPU: jump -scanlimited 0x800000 +# } + +# These commands will be run when use SPL and will be skipped if no spl +# if (SPL support SDPV) +# { +SDPV: delay 1000 +SDPV: write -f _flash.bin -skipspl -scanterm -scanlimited 0x800000 +SDPV: jump -scanlimited 0x800000 +# } + +FB: ucmd setenv fastboot_dev mmc +FB: ucmd setenv mmcdev ${sd_dev} +FB: ucmd mmc dev ${sd_dev} +FB: flash -raw2sparse all _image +FB: flash -scanterm -scanlimited 0x800000 bootloader _flash.bin +FB: done diff --git a/uuu/sd_burn_loader.lst b/uuu/sd_burn_loader.lst new file mode 100644 index 0000000..4acb0c7 --- /dev/null +++ b/uuu/sd_burn_loader.lst @@ -0,0 +1,33 @@ +uuu_version 1.2.39 + +# @_flash.bin | bootloader +# @_image [_flash.bin] | image burn to emmc, default is the same as bootloader + +# This command will be run when i.MX6/7 i.MX8MM, i.MX8MQ +SDP: boot -f _flash.bin + +# This command will be run when ROM support stream mode +# i.MX8QXP, i.MX8QM +SDPS: boot -f _flash.bin + +# These commands will be run when use SPL and will be skipped if no spl +# SDPU will be deprecated. please use SDPV instead of SDPU +# { +SDPU: delay 1000 +SDPU: write -f _flash.bin -offset 0x57c00 +SDPU: jump +# } + +# These commands will be run when use SPL and will be skipped if no spl +# if (SPL support SDPV) +# { +SDPV: delay 1000 +SDPV: write -f _flash.bin -skipspl +SDPV: jump +# } + +FB: ucmd setenv fastboot_dev mmc +FB: ucmd setenv mmcdev ${sd_dev} +FB: ucmd mmc dev ${sd_dev} +FB: flash bootloader _image +FB: Done diff --git a/uuu/spl_boot.lst b/uuu/spl_boot.lst new file mode 100644 index 0000000..d005a53 --- /dev/null +++ b/uuu/spl_boot.lst @@ -0,0 +1,26 @@ +uuu_version 1.2.39 + +# This command will be run when i.MX6/7 i.MX8MM, i.MX8MQ +SDP: boot -f _flash.bin + +# This command will be run when ROM support stream mode +# i.MX8QXP, i.MX8QM +SDPS: boot -f _flash.bin + +# These commands will be run when use SPL and will be skipped if no spl +# SDPU will be deprecated. please use SDPV instead of SDPU +# { +SDPU: delay 1000 +SDPU: write -f _flash.bin -offset 0x57c00 +SDPU: jump +SDPU: done +# } + +# These commands will be run when use SPL and will be skipped if no spl +# if (SPL support SDPV) +# { +SDPV: delay 1000 +SDPV: write -f _flash.bin -skipspl +SDPV: jump +SDPV: done +# } \ No newline at end of file diff --git a/uuu/uuu.cpp b/uuu/uuu.cpp new file mode 100644 index 0000000..bc9ed2d --- /dev/null +++ b/uuu/uuu.cpp @@ -0,0 +1,1124 @@ +/* +* Copyright 2018 NXP. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* Neither the name of the NXP Semiconductor nor the names of its +* contributors may be used to endorse or promote products derived from this +* software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "buildincmd.h" +#include +#include + +#include "../libuuu/libuuu.h" + +const char * g_vt_yellow = "\x1B[93m"; +const char * g_vt_default = "\x1B[0m"; +const char * g_vt_green = "\x1B[92m"; +const char * g_vt_red = "\x1B[91m"; +const char * g_vt_kcyn = "\x1B[36m"; +const char * g_vt_boldwhite = "\x1B[97m"; + +void clean_vt_color() noexcept +{ + g_vt_yellow = ""; + g_vt_default = g_vt_yellow; + g_vt_green = g_vt_yellow; + g_vt_red = g_vt_yellow; + g_vt_kcyn = g_vt_yellow; + g_vt_boldwhite = g_vt_yellow; +} + +using namespace std; + +int get_console_width(); +void print_oneline(string str); +int auto_complete(int argc, char**argv); +void print_autocomplete_help(); + +char g_sample_cmd_list[] = { +#include "uuu.clst" +}; + +vector g_usb_path_filter; + +int g_verbose = 0; +static bool g_start_usb_transfer; + +class AutoCursor +{ +public: + ~AutoCursor() + { + printf("\x1b[?25h\n\n\n"); + } +}; + +void ctrl_c_handle(int) +{ + do { + AutoCursor a; + } while(0); + + exit(1); +} + +class string_ex : public std::string +{ +public: + int format(const char *fmt, ...) + { + va_list args; + va_start(args, fmt); + size_t len = std::vsnprintf(NULL, 0, fmt, args); + va_end(args); + + this->resize(len); + + va_start(args, fmt); + std::vsnprintf((char*)c_str(), len + 1, fmt, args); + va_end(args); + + return 0; + } +}; + +void print_help(bool detail = false) +{ + const char help[] = + "uuu [-d -m -v -V] <" "bootloader|cmdlists|cmd" ">\n\n" + " bootloader download bootloader to board by usb\n" + " cmdlist run all commands in cmdlist file\n" + " If it is path, search uuu.auto in dir\n" + " If it is zip, search uuu.auto in zip\n" + " cmd Run one command, use -H see detail\n" + " example: SDPS: boot -f flash.bin\n" + " -d Daemon mode, wait for forever.\n" + " -v -V verbose mode, -V enable libusb error\\warning info\n" + " -dry Dry run mode, check if script or cmd correct \n" + " -m USBPATH Only monitor these paths.\n" + " -m 1:2 -m 1:3\n\n" + " -t Timeout second for wait known usb device appeared\n" + " -T Timeout second for wait next known usb device appeared at stage switch\n" + " -e set environment variable key=value\n" + " -pp usb polling period in milliseconds\n" + "uuu -s Enter shell mode. uuu.inputlog record all input commands\n" + " you can use \"uuu uuu.inputlog\" next time to run all commands\n\n" + "uuu -udev linux: show udev rule to avoid sudo each time \n" + "uuu -lsusb List connected know devices\n" + "uuu -IgSerNum Set windows registry to ignore USB serial number for known uuu devices\n" + "uuu -h show general help\n" + "uuu -H show general help and detailed help for commands\n\n"; + printf("%s", help); + printf("uuu [-d -m -v] -b[run] "); + g_BuildScripts.ShowCmds(); + printf(" arg...\n"); + printf("\tRun Built-in scripts\n"); + g_BuildScripts.ShowAll(); + printf("\nuuu -bshow "); + g_BuildScripts.ShowCmds(); + printf("\n"); + printf("\tShow built-in script\n"); + printf("\n"); + + print_autocomplete_help(); + + if (detail == false) + return; + + size_t start = 0, pos = 0; + string str= g_sample_cmd_list; + + bool bprint = false; + while ((pos = str.find('\n',pos)) != str.npos) + { + string s = str.substr(start, pos - start); + if (s.substr(0, 6) == "# ----") + bprint = true; + + if (bprint) + { + if (s[0] == '#') + { + printf("%s\n", &(s[1])); + } + } + pos += 1; + start = pos; + } +} +void print_version() +{ + printf("uuu (Universal Update Utility) for nxp imx chips -- %s\n\n", uuu_get_version_string()); +} + +int print_cfg(const char *pro, const char * chip, const char * /*compatible*/, uint16_t pid, uint16_t vid, uint16_t bcdmin, uint16_t bcdmax, void * /*p*/) +{ + const char *ext; + if (strlen(chip) >= 7) + ext = ""; + else + ext = "\t"; + + if (bcdmin == 0 && bcdmax == 0xFFFF) + printf("\t%s\t %s\t%s 0x%04x\t 0x%04x\n", pro, chip, ext, pid, vid); + else + printf("\t%s\t %s\t%s 0x%04x\t 0x%04x\t [0x%04x..0x%04x]\n", pro, chip, ext, pid, vid, bcdmin, bcdmax); + return 0; +} + +int print_udev_rule(const char * /*pro*/, const char * /*chip*/, const char * /*compatible*/, + uint16_t vid, uint16_t pid, uint16_t /*bcdmin*/, uint16_t /*bcdmax*/, void * /*p*/) +{ + printf("SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"%04x\", ATTRS{idProduct}==\"%04x\", TAG+=\"uaccess\"\n", + vid, pid); + return 0; +} + +int polling_usb(std::atomic& bexit); + +int g_overall_status; +int g_overall_okay; +int g_overall_failure; +char g_wait[] = "|/-\\"; +int g_wait_index; + + +string build_process_bar(size_t width, size_t pos, size_t total) +{ + string str; + str.resize(width, ' '); + str[0] = '['; + str[width - 1] = ']'; + + if (total == 0) + { + if (pos == 0) + return str; + + string_ex loc; + size_t s = pos / (1024 * 1024); + loc.format("%dM", s); + str.replace(1, loc.size(), loc); + return str; + } + + size_t i; + + if (pos > total) + pos = total; + + for (i = 1; i < (width-2) * pos / total; i++) + { + str[i] = '='; + } + + if (i > 1) + str[i] = '>'; + + if (pos == total) + str[str.size() - 2] = '='; + + string_ex per; + per.format("%d%%", pos * 100 / total); + + size_t start = (width - per.size()) / 2; + str.replace(start, per.size(), per); + str.insert(start, g_vt_yellow); + str.insert(start + per.size() + strlen(g_vt_yellow), g_vt_default); + return str; +} + +void print_auto_scroll(string str, size_t len, size_t start) +{ + if (str.size() <= len) + { + str.resize(len, ' '); + cout << str; + return; + } + + if(str.size()) + start = start % str.size(); + else + start = 0; + + string s = str.substr(start, len); + s.resize(len, ' '); + cout << s; +} +class ShowNotify +{ +public: + string m_cmd; + string m_dev; + size_t m_trans_pos = 0; + int m_status = 0; + size_t m_cmd_total = 0; + size_t m_cmd_index = 0; + string m_last_err; + int m_done = 0; + size_t m_start_pos = 0; + size_t m_trans_size = 0; + clock_t m_start_time; + uint64_t m_cmd_start_time; + uint64_t m_cmd_end_time; + bool m_IsEmptyLine = false; + + ShowNotify() : m_start_time{clock()} {} + + bool update(uuu_notify nt) + { + if (nt.type == uuu_notify::NOFITY_DEV_ATTACH) + { + m_dev = nt.str; + m_done = 0; + m_status = 0; + } + if (nt.type == uuu_notify::NOTIFY_CMD_START) + { + m_start_pos = 0; + m_cmd = nt.str; + m_cmd_start_time = nt.timestamp; + } + if (nt.type == uuu_notify::NOTIFY_DECOMPRESS_START) + { + m_start_pos = 0; + m_cmd = nt.str; + m_cmd_start_time = nt.timestamp; + m_dev = "Prep"; + } + if (nt.type == uuu_notify::NOTIFY_DOWNLOAD_START) + { + m_start_pos = 0; + m_cmd = nt.str; + m_cmd_start_time = nt.timestamp; + m_dev = "Prep"; + } + if (nt.type == uuu_notify::NOTIFY_DOWNLOAD_END) + { + m_IsEmptyLine = true; + } + if (nt.type == uuu_notify::NOTIFY_TRANS_SIZE || nt.type == uuu_notify::NOTIFY_DECOMPRESS_SIZE) + { + m_trans_size = nt.total; + return false; + } + if (nt.type == uuu_notify::NOTIFY_CMD_TOTAL) + { + m_cmd_total = nt.total; + return false; + } + if (nt.type == uuu_notify::NOTIFY_CMD_INDEX) + { + m_cmd_index = nt.index; + return false; + } + if (nt.type == uuu_notify::NOTIFY_DONE) + { + if (m_status) + g_overall_failure++; + else + g_overall_okay++; + + m_done = 1; + } + if (nt.type == uuu_notify::NOTIFY_CMD_END) + { + m_cmd_end_time = nt.timestamp; + if(nt.status) + { + g_overall_status = nt.status; + m_last_err = uuu_get_last_err_string(); + } + m_status |= nt.status; + if (m_status) + g_overall_failure++; + } + if (nt.type == uuu_notify::NOTIFY_TRANS_POS || nt.type == uuu_notify::NOTIFY_DECOMPRESS_POS) + { + if (m_trans_size == 0) { + + m_trans_pos = nt.index; + return true; + } + + if ((nt.index - m_trans_pos) < (m_trans_size / 100) + && nt.index != m_trans_size) + return false; + + m_trans_pos = nt.index; + } + + return true; + } + void print_verbose(uuu_notify*nt) + { + if (this->m_dev == "Prep" && g_start_usb_transfer) + return; + + if (nt->type == uuu_notify::NOFITY_DEV_ATTACH) + { + cout << "New USB Device Attached at " << nt->str << endl; + } + if (nt->type == uuu_notify::NOTIFY_CMD_START) + { + cout << m_dev << ">" << "Start Cmd:" << nt->str << endl; + } + if (nt->type == uuu_notify::NOTIFY_CMD_END) + { + double diff = m_cmd_end_time - m_cmd_start_time; + diff /= 1000; + if (nt->status) + { + cout << m_dev << ">" << g_vt_red <<"Fail " << uuu_get_last_err_string() << "("<< std::setprecision(4) << diff << "s)" << g_vt_default << endl; + } + else + { + cout << m_dev << ">" << g_vt_green << "Okay ("<< std::setprecision(4) << diff << "s)" << g_vt_default << endl; + } + } + + if (nt->type == uuu_notify::NOTIFY_TRANS_POS || nt->type == uuu_notify::NOTIFY_DECOMPRESS_POS) + { + if (m_trans_size) + cout << g_vt_yellow << "\r" << m_trans_pos * 100 / m_trans_size <<"%" << g_vt_default; + else + cout << "\r" << m_trans_pos; + + cout.flush(); + } + + if (nt->type == uuu_notify::NOTIFY_CMD_INFO) + cout << nt->str; + + if (nt->type == uuu_notify::NOTIFY_WAIT_FOR) + cout << "\r" << nt->str << " "<< g_wait[((g_wait_index++) & 0x3)]; + + if (nt->type == uuu_notify::NOTIFY_DECOMPRESS_START) + cout << "Decompress file:" << nt->str << endl; + + if (nt->type == uuu_notify::NOTIFY_DOWNLOAD_START) + cout << "Download file:" << nt->str << endl; + + } + void print(int verbose = 0, uuu_notify*nt=NULL) + { + verbose ? print_verbose(nt) : print_simple(); + } + string get_print_dev_string() + { + string str; + str = m_dev; + str.resize(8, ' '); + + string_ex s; + s.format("%2d/%2d", m_cmd_index+1, m_cmd_total); + + str += s; + return str; + } + void print_simple() + { + int width = get_console_width(); + int info, bar; + info = 14; + bar = 40; + + if (m_IsEmptyLine) + { + string str(width, ' '); + cout << str; + return; + } + if (width <= bar + info + 3) + { + string_ex str; + + str += get_print_dev_string(); + + str += g_wait[(g_wait_index++) & 0x3]; + + print_oneline(str); + return ; + } + else + { + string_ex str; + str += get_print_dev_string(); + + str.resize(info, ' '); + cout << str; + + if (m_done || m_status) + { + string str; + str.resize(bar, ' '); + str[0] = '['; + str[str.size() - 1] = ']'; + string err; + if (m_status) + { + err = uuu_get_last_err_string(); + err.resize(bar - 2, ' '); + str.replace(1, err.size(), err); + str.insert(1, g_vt_red); + str.insert(1 + strlen(g_vt_red) + err.size(), g_vt_default); + } + else + { + str.replace(1, 4, "Done"); + str.insert(1, g_vt_green); + str.insert(1 + strlen(g_vt_green) + strlen("Done"), g_vt_default); + } + cout << str; + } else { + cout << build_process_bar(bar, m_trans_pos, m_trans_size); + } + cout << " "; + print_auto_scroll(m_cmd, width - bar - info-1, m_start_pos); + +if (clock() - m_start_time > CLOCKS_PER_SEC / 4) +{ + m_start_pos++; + m_start_time = clock(); +} +cout << endl; + +return; + } + } +}; + +static map g_map_path_nt; +mutex g_callback_mutex; + +void print_oneline(string str) +{ + size_t w = get_console_width(); + if (w <= 3) + return; + + if (str.size() >= w) + { + str.resize(w - 1); + str[str.size() - 1] = '.'; + str[str.size() - 2] = '.'; + str[str.size() - 3] = '.'; + } + else + { + str.resize(w, ' '); + } + cout << str << endl; + +} + +ShowNotify Summary(map *np) +{ + ShowNotify sn; + for (auto it = np->begin(); it != np->end(); it++) + { + if (it->second.m_dev == "Prep") + { + sn.m_trans_size += it->second.m_trans_size; + sn.m_trans_pos += it->second.m_trans_pos; + } + else + { + if (it->second.m_trans_pos || it->second.m_cmd_index) + g_start_usb_transfer = true; // Hidden HTTP download when USB start transfer + } + } + + if(g_start_usb_transfer) + sn.m_IsEmptyLine = true; // Hidden HTTP download when USB start transfer + + sn.m_dev = "Prep"; + sn.m_cmd = "Http Download\\Uncompress"; + return sn; +} + +int progress(uuu_notify nt, void *p) +{ + map *np = (map*)p; + map::iterator it; + + std::lock_guard lock(g_callback_mutex); + + if ((*np)[nt.id].update(nt)) + { + if (!(*np)[nt.id].m_dev.empty()) + if ((*np)[nt.id].m_dev != "Prep") + g_map_path_nt[(*np)[nt.id].m_dev] = (*np)[nt.id]; + + if (g_verbose) + { + if((*np)[nt.id].m_dev == "Prep") + Summary(np).print(g_verbose, &nt); + else + (*np)[nt.id].print(g_verbose, &nt); + } + else + { + string_ex str; + str.format("\rSuccess %d Failure %d ", g_overall_okay, g_overall_failure); + + if (g_map_path_nt.empty()) + str += "Wait for Known USB Device Appear..."; + + if (!g_usb_path_filter.empty()) + { + str += " at path "; + for (size_t i = 0; i < g_usb_path_filter.size(); i++) + str += g_usb_path_filter[i] + " "; + } + + print_oneline(str); + print_oneline(""); + if ((*np)[nt.id].m_dev == "Prep" && !g_start_usb_transfer) + { + Summary(np).print(); + }else + print_oneline(""); + + for (it = g_map_path_nt.begin(); it != g_map_path_nt.end(); it++) + it->second.print(); + + for (size_t i = 0; i < g_map_path_nt.size() + 3; i++) + cout << "\x1B[1F"; + + } + + //(*np)[nt.id] = g_map_path_nt[(*np)[nt.id].m_dev]; + } + + if (nt.type == uuu_notify::NOTIFY_THREAD_EXIT) + { + if(np->find(nt.id) != np->end()) + np->erase(nt.id); + } + return 0; +} +#ifdef _MSC_VER + +#define DEFINE_CONSOLEV2_PROPERTIES +#include +#include +#include + +bool enable_vt_mode() +{ + // Set output mode to handle virtual terminal sequences + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + { + clean_vt_color(); + return false; + } + + DWORD dwMode = 0; + if (!GetConsoleMode(hOut, &dwMode)) + { + clean_vt_color(); + return false; + } + + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(hOut, dwMode)) + { + clean_vt_color(); + return false; + } + return true; +} + +int get_console_width() +{ + CONSOLE_SCREEN_BUFFER_INFO sbInfo; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &sbInfo); + return sbInfo.dwSize.X; +} +#else +#include +bool enable_vt_mode() { return true; } +int get_console_width() +{ + struct winsize w; + ioctl(0, TIOCGWINSZ, &w); + return w.ws_col; +} +#endif +void print_usb_filter() +{ + if (!g_usb_path_filter.empty()) + { + cout << " at path "; + for (size_t i = 0; i < g_usb_path_filter.size(); i++) + cout << g_usb_path_filter[i] << " "; + } +} + +int runshell(int shell) +{ + int uboot_cmd = 0; + string prompt = "U>"; + + if (shell) + { + cout << "Please input command: " << endl; + string cmd; + ofstream log("uuu.inputlog", ofstream::binary); + log << "uuu_version " + << ((uuu_get_version() & 0xFF0000) >> 16) + << "." + << ((uuu_get_version() & 0xFF00) >> 8) + << "." + << ((uuu_get_version() & 0xFF)) + << endl; + while (1) + { + cout << prompt; + getline(cin, cmd); + + if (cmd == "uboot") + { + uboot_cmd = 1; + prompt = "=>"; + cout << "Enter into u-boot cmd mode" << endl; + cout << "Okay" << endl; + } + else if (cmd == "exit" && uboot_cmd == 1) + { + uboot_cmd = 0; + prompt = "U>"; + cout << "Exit u-boot cmd mode" << endl; + cout << "Okay" << endl; + }else if (cmd == "help" || cmd == "?") + { + print_help(); + } + else if (cmd == "q" || cmd == "quit") + { + return 0; + } + else + { + log << cmd << endl; + log.flush(); + + if (uboot_cmd) + cmd = "fb: ucmd " + cmd; + + int ret = uuu_run_cmd(cmd.c_str(), 0); + if (ret) + cout << uuu_get_last_err_string() << endl; + else + cout << "Okay" << endl; + } + } + return 0; + } + + return -1; +} + +void print_udev() +{ + uuu_for_each_cfg(print_udev_rule, NULL); + fprintf(stderr, "\n1: put above udev run into /etc/udev/rules.d/70-uuu.rules\n"); + fprintf(stderr, "\tsudo sh -c \"uuu -udev >> /etc/udev/rules.d/70-uuu.rules\"\n"); + fprintf(stderr, "2: update udev rule\n"); + fprintf(stderr, "\tsudo udevadm control --reload\n"); +} + +int print_usb_device(const char *path, const char *chip, const char *pro, uint16_t vid, uint16_t pid, uint16_t bcd, void * /*p*/) +{ + printf("\t%s\t %s\t %s\t 0x%04X\t0x%04X\t 0x%04X\n", path, chip, pro, vid, pid, bcd); + return 0; +} + +void print_lsusb() +{ + cout << "Connected Known USB Devices\n"; + printf("\tPath\t Chip\t Pro\t Vid\t Pid\t BcdVersion\n"); + printf("\t==================================================\n"); + + uuu_for_each_devices(print_usb_device, NULL); +} + +#ifdef WIN32 +int ignore_serial_number(const char *pro, const char *chip, const char */*comp*/, uint16_t vid, uint16_t pid, uint16_t /*bcdlow*/, uint16_t /*bcdhigh*/, void */*p*/) +{ + printf("\t %s\t %s\t 0x%04X\t0x%04X\n", chip, pro, vid, pid); + + char sub[128]; + snprintf(sub, 128, "IgnoreHWSerNum%04x%04x", vid, pid); + const BYTE value = 1; + + LSTATUS ret = RegSetKeyValueA(HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\UsbFlags", + sub, REG_BINARY, &value, 1); + if(ret == ERROR_SUCCESS) + return 0; + + printf("Set key failure, try run as administrator permission\n"); + return -1; +} +#endif + +int set_ignore_serial_number() +{ +#ifndef WIN32 + printf("Only windows system need set ignore serial number registry"); + return -1; +#else + printf("Set window registry to ignore usb hardware serial number for known uuu device:\n"); + return uuu_for_each_cfg(ignore_serial_number, NULL); +#endif +} + +int main(int argc, char **argv) +{ + if (auto_complete(argc, argv) == 0) + return 0; + + if (argc >= 2) + { + string s = argv[1]; + if(s == "-udev") + { + print_udev(); + return 0; + } + if (s == "-bshow") + { + if (2 == argc || g_BuildScripts.find(argv[2]) == g_BuildScripts.end()) + { + fprintf(stderr, "Error, must be have script name: "); + g_BuildScripts.ShowCmds(stderr); + fprintf(stderr,"\n"); + return -1; + } + else + { + string str = g_BuildScripts[argv[2]].m_text; + while (str.size() > 0 && (str[0] == '\n' || str[0] == ' ')) + str = str.erase(0,1); + + printf("%s", str.c_str()); + return 0; + } + } + } + + AutoCursor a; + + print_version(); + + if (!enable_vt_mode()) + { + cout << "Your console don't support VT mode, fail back to verbose mode" << endl; + g_verbose = 1; + } + + if (argc == 1) + { + print_help(); + return 0; + } + + int deamon = 0; + int shell = 0; + string filename; + string cmd; + int ret; + int dryrun = 0; + + string cmd_script; + + for (int i = 1; i < argc; i++) + { + string s = argv[i]; + if (!s.empty() && s[0] == '-') + { + if (s == "-d") + { + deamon = 1; + }else if (s == "-s") + { + shell = 1; + g_verbose = 1; + } + else if (s == "-v") + { + g_verbose = 1; + } + else if (s == "-V") + { + g_verbose = 1; + uuu_set_debug_level(2); + }else if (s == "-dry") + { + dryrun = 1; + g_verbose = 1; + } + else if (s == "-h") + { + print_help(false); + return 0; + } + else if (s == "-H") + { + print_help(true); + return 0; + } + else if (s == "-m") + { + i++; + uuu_add_usbpath_filter(argv[i]); + g_usb_path_filter.push_back(argv[i]); + } + else if (s == "-t") + { + i++; + uuu_set_wait_timeout(atoll(argv[i])); + } + else if (s == "-T") + { + i++; + uuu_set_wait_next_timeout(atoll(argv[i])); + } + else if (s == "-pp") + { + i++; + uuu_set_poll_period(atoll(argv[i])); + } + else if (s == "-lsusb") + { + print_lsusb(); + return 0; + } + else if (s == "-IgSerNum") + { + return set_ignore_serial_number(); + } + else if (s == "-e") + { +#ifndef WIN32 + #define _putenv putenv +#endif + i++; + if (_putenv(argv[i])) + { + printf("error, failed to set '%s', environment parameter must have the form key=value\n", argv[i]); + return -1; + } + } + else if (s == "-b" || s == "-brun") + { + if (i + 1 == argc) + { + printf("error, must be have script name: "); + g_BuildScripts.ShowCmds(); + printf("\n"); + return -1; + } + + vector args; + for (int j = i + 2; j < argc; j++) + { + string s = argv[j]; + if (s.find(' ') != string::npos) + { + s.insert(s.begin(), '"'); + s.insert(s.end(), '"'); + } + args.push_back(s); + } + + // if script name is not build-in, try to look for a file + if (g_BuildScripts.find(argv[i + 1]) == g_BuildScripts.end()) { + const string tmpCmdFileName{argv[i + 1]}; + + std::ifstream t(tmpCmdFileName); + std::string fileContents((std::istreambuf_iterator(t)), + std::istreambuf_iterator()); + + if (fileContents.empty()) { + printf("%s is not built-in script or fail load external script file", tmpCmdFileName.c_str()); + return -1; + } + + BuiltInScriptRawData tmpCmd{ + tmpCmdFileName.c_str(), + fileContents.c_str(), + "Script loaded from file" + }; + + g_BuildScripts.emplace(tmpCmdFileName, &tmpCmd); + + cmd_script = g_BuildScripts[tmpCmdFileName].replace_script_args(args); + } + else { + cmd_script = g_BuildScripts[argv[i + 1]].replace_script_args(args); + } + break; + } + else + { + cout << "Unknown option: " << s.c_str(); + return -1; + } + }else if (!s.empty() && s[s.size() - 1] == ':') + { + for (int j = i; j < argc; j++) + { + s = argv[j]; + if (s.find(' ') != string::npos && s[s.size() - 1] != ':') + { + s.insert(s.begin(), '"'); + s.insert(s.end(), '"'); + } + cmd.append(s); + if(j != (argc -1)) /* Don't add space at last arg */ + cmd.append(" "); + } + break; + } + else + { + filename = s; + break; + } + } + + signal(SIGINT, ctrl_c_handle); + + if (deamon && shell) + { + printf("Error: -d -s Can't apply at the same time\n"); + return -1; + } + + if (deamon && dryrun) + { + printf("Error: -d -dry Can't apply at the same time\n"); + return -1; + } + + if (shell && dryrun) + { + printf("Error: -dry -s Can't apply at the same time\n"); + return -1; + } + + if (g_verbose) + { + printf("%sBuild in config:%s\n", g_vt_boldwhite, g_vt_default); + printf("\tPctl\t Chip\t\t Vid\t Pid\t BcdVersion\n"); + printf("\t==================================================\n"); + uuu_for_each_cfg(print_cfg, NULL); + + if (!cmd_script.empty()) + printf("\n%sRun built-in script:%s\n %s\n\n", g_vt_boldwhite, g_vt_default, cmd_script.c_str()); + + if (!shell) + cout << "Wait for Known USB Device Appear..."; + + print_usb_filter(); + + printf("\n"); + } + else { + cout << "Wait for Known USB Device Appear..."; + print_usb_filter(); + cout << "\r"; + cout << "\x1b[?25l"; + cout.flush(); + } + + map nt_session; + + uuu_register_notify_callback(progress, &nt_session); + + + if (!cmd.empty()) + { + ret = uuu_run_cmd(cmd.c_str(), dryrun); + + for (size_t i = 0; i < g_map_path_nt.size()+3; i++) + printf("\n"); + if(ret) + printf("\nError: %s\n", uuu_get_last_err_string()); + else + printf("Okay\n"); + + runshell(shell); + return ret; + } + + if (!cmd_script.empty()) + ret = uuu_run_cmd_script(cmd_script.c_str(), dryrun); + else + ret = uuu_auto_detect_file(filename.c_str()); + + if (ret) + { + ret = runshell(shell); + if(ret) + cout << g_vt_red << "\nError: " << g_vt_default << uuu_get_last_err_string(); + return ret; + } + + if (uuu_wait_uuu_finish(deamon, dryrun)) + { + cout << g_vt_red << "\nError: " << g_vt_default << uuu_get_last_err_string(); + return -1; + } + + runshell(shell); + + /*Wait for the other thread exit, after send out CMD_DONE*/ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if(!g_verbose) + printf("\n\n\n"); + return g_overall_status; +} diff --git a/uuu/uuu.lst b/uuu/uuu.lst new file mode 100644 index 0000000..f5ce87a --- /dev/null +++ b/uuu/uuu.lst @@ -0,0 +1,70 @@ +uuu_version 1.0.0 +# +# uuu(universal update utility) command list file +# Firstly line must be uuu_version show minimize uuu host version number +# --------------------------------------------------------------------- +# Command Format PROTOCOL COMMAND ARG +# PROTOCOL +# ALL protocol supported common command +# done #last command for whole flow +# delay # delay ms +# sh\shell #Run shell command, such as wget to file from network +# < #use shell command's output as uuu command +# this command generally used for burn some sequence number, such production id, mac address +# for example: +# FB:< echo ucmd print +# +# CFG: Config protocol of specific usb device vid/pid +# SDPS|SDP|FB\Fastboot|FBK -chip -pid -vid [-bcdversion ] +# +# SDPS: Stream download after MX8QXPB0 +# boot -f [-offset 0x0000] +# +# SDP: iMX6/iMX7 HID download protocol. +# dcd -f +# write -f [-addr 0x000000] [-ivt 0] +# jump -f [-ivt 0] +# boot -f [-nojump] +# rdmem -addr -format <8|16|32> +# wrmem -addr -format <8|16|32> -value +# +# FB[-t timeout]:\Fastboot: android fastboot protocol. unit of timeout is ms +# getvar +# ucmd +# acmd +# flash [-raw2sparse] +# download -f +# +# FBK: community with kernel with fastboot protocol. DO NOT compatible with fastboot tools. +# ucmd and wait for command finish +# acmd don't wait for command finish +# sync wait for acmd process finish. +# ucp copy file from/to target +# T: means target board file. +# T:- means copy data to target's stdio pipe. +# copy image T:/root/image ;download image to path /root/image +# copy T:/root/image image ;upload /root/image to file image. +# Example for transfer big file +# acmd tar - ; run tar background and get data from stdio +# ucp rootfs.tar.gz T:- ; send to target stdio pipe +# sync ; wait for tar process exit. +# +# For example: +# SDPS: boot -f +# SDP: boot -f +# CFG: SDP: -chip imx6ull -pid 0x1234 -vid 0x5678 +# +# SDP: boot -f u-boot-imx7dsabresd_sd.imx -nojump +# SDP: write -f zImage -addr 0x80800000 +# SDP: write -f zImage-imx7d-sdb.dtb -addr 0x83000000 +# SDP: write -f fsl-image-mfgtool-initramfs-imx_mfgtools.cpio.gz.u-boot -addr 0x83800000 +# SDP: jump -f u-boot-dtb.imx -ivt + +CFG: FB: -vid 0x18D1 -pid 0x0D02 +SDP: boot -f u-boot-dtb.imx +FB: getvar version +FB: ucmd setenv fastboot_buffer 0x80800000 +FB: download -f zImage +FB: ucmd setenv fastboot_buffer 0x83000000 +FB: download -f zImage-imx7d-sdb.dtb +FB: ucmd bootz 0x80800000 - 0x83000000 -- 2.30.2