diff --git a/README.en.md b/README.en.md index 503e509..a54b221 100644 --- a/README.en.md +++ b/README.en.md @@ -44,6 +44,7 @@ In the configuration menu, you can select the following packages (all under Luci | **QMI Driver Selection** | Choose between Generic QMI driver or Vendor QMI driver. | | **Quectel Connect Manager Selection** | Choose one of:
- Tom customized Quectel CM: With options to block default route addition and resolv.conf modification
- QWRT quectel-CM-5G: Uses QWRT's quectel-CM-5G
- NORMAL quectel-cm: Uses the standard quectel-cm | | **Add PCIe Modem SUPPORT** | Select PCIe driver, requires kmod_mhi in feeds. | +| **Add Qfirehose SUPPORT** | Add Qfirehose support for Qualcomm chip module firmware upgrade. | | **luci-app-qmodem-hc** | Supports hc-g80 SIM card switching, exclusive to specific devices. | | **luci-app-qmodem-mwan** | Supports multi-WAN settings. | | **luci-app-qmodem-sms** | SMS sending feature. | diff --git a/README.md b/README.md index 33be5bb..0ffdddc 100644 --- a/README.md +++ b/README.md @@ -44,15 +44,15 @@ make menuconfig | -------------------------------------------- | :------------------: | | **luci-app-qmodem**| 有模组信息、拨号设置、高级设置三大功能块。由于主程序在这里,因此其他功能依赖该程序(原谅我将后端程序也放在了这里)。| | **Add Lua Luci Homepage**| 添加 Lua Luci 首页。luci2(Js Luci)首页默认已添加,若使用luci2时勾选了这个,会有两个首页| -| **QMI驱动选择**| 可选择 Generic QMI driver(通用QMI驱动)或 Vendor QMI driver(厂商QMI驱动)| -| **Quectel Connect Manager选择**| 可选择以下三种之一:
- Tom customized Quectel CM:使用本仓库定制的Quectel CM,支持屏蔽添加默认路由、屏蔽修改resolv.conf等功能
- QWRT Quectel-CM-5G:使用 QWRT 仓库的 quectel-CM-5G
- NORMAL Quectel-CM:使用普通的 quectel-cm| +| **QMI Driver Selection**| 可选择 Generic QMI driver(通用QMI驱动)或 Vendor QMI driver(厂商QMI驱动)| +| **Quectel Connect Manager Selection**| 可选择以下三种之一:
- Tom customized Quectel CM:使用本仓库定制的Quectel CM,支持屏蔽添加默认路由、屏蔽修改resolv.conf等功能
- QWRT Quectel-CM-5G:使用 QWRT 仓库的 quectel-CM-5G
- NORMAL Quectel-CM:使用普通的 quectel-cm| | **Add PCIe Modem SUPPORT**| 勾选 PCIe 驱动,需要feeds里有kmod_mhi| +| **Add Qfirehose SUPPORT**| 添加 Qfirehose 支持,用于高通芯片模组固件升级| | **luci-app-qmodem-hc**| 支持 hc-g80 SIM 卡切换,该插件为设备专属插件| | **luci-app-qmodem-mwan**| 支持多 WAN 设置。| | **luci-app-qmodem-sms**| 短信首发功能| | **luci-app-qmodem-ttl**| TTL 重写功能| - # 项目介绍 ## 为什么选择该项目 diff --git a/application/qfirehose/Makefile b/application/qfirehose/Makefile new file mode 100644 index 0000000..bf04e6a --- /dev/null +++ b/application/qfirehose/Makefile @@ -0,0 +1,52 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=qfirehose +PKG_VERSION:=1.4.21 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=Oskari Rauta +PKG_LICENSE:= +PKG_LICENSE_FILES:=NOTICE + +include $(INCLUDE_DIR)/package.mk + +define Package/qfirehose + SECTION:=utils + CATEGORY:=Utilities + TITLE:=Quectel Firehose Recovery application + URL:=https://github.com/nippynetworks/qfirehose +endef + +define Package/qfirehose/description + Utility that is able to flash firmwares on Quectel's modems. + Usage: qfirehose -f FW_PATH + + Warning. + + - Use of software is completely on your own risk. + Flashing wrong firmware or failed flash can brick your modem permanently. + Avoid flashing, if device works without issues and updated firmware does not contain new necessary changes. + Do not flash, if you are not willing to take this risk or do not know what you are doing. + + - After succesful flashing, you should use terminal to issue factory reset for modem settings with AT&F command. + + - mPCIe users (mostly): If modem has completely disappeared after succesful flashing, reason might be that some firmware updates + set default mode to USB3 which is unsupported by some mPCIe slots, in this case, you should connect it to USB + port using mPCIe -> USB adapter, even most of cheap chinese modules can reveal device. After this you should issue + a command to use USB2, which may vary between models, but on most Quectel modems is: AT+QUSBCFG="SS",0 + Changing value on end of AT command 0 to 1, selects USB3 instead. Refer to documents of your modem. +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +MAKE_ARGS += linux + +define Package/qfirehose/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/out/QFirehose $(1)/usr/bin/qfirehose +endef + +$(eval $(call BuildPackage,qfirehose)) diff --git a/application/qfirehose/src/.clang-format b/application/qfirehose/src/.clang-format new file mode 100644 index 0000000..3e633a0 --- /dev/null +++ b/application/qfirehose/src/.clang-format @@ -0,0 +1,167 @@ +--- +BasedOnStyle: Google +--- +Language: Cpp +AccessModifierOffset: -4 +# AlignAfterOpenBracket: Align +# AlignConsecutiveMacros: false +# AlignConsecutiveAssignments: false +# AlignConsecutiveDeclarations: false +# AlignEscapedNewlines: Left +# AlignOperands: true +# AlignTrailingComments: true +# AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: false +# AllowAllParametersOfDeclarationOnNextLine: true +# AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: true +# AllowShortFunctionsOnASingleLine: All +# AllowShortLambdasOnASingleLine: All +# AllowShortIfStatementsOnASingleLine: WithoutElse +# AllowShortLoopsOnASingleLine: true +# AlwaysBreakAfterDefinitionReturnType: None +# AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +# AlwaysBreakTemplateDeclarations: Yes +# BinPackArguments: true +# BinPackParameters: true +BraceWrapping: + # AfterCaseLabel: false + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + # AfterNamespace: false + # AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + # IndentBraces: false + # SplitEmptyFunction: true + # SplitEmptyRecord: true + # SplitEmptyNamespace: true +# BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +# BreakBeforeInheritanceComma: false +# BreakInheritanceList: BeforeColon +# BreakBeforeTernaryOperators: true +# BreakConstructorInitializersBeforeComma: false +# BreakConstructorInitializers: BeforeColon +# BreakAfterJavaFieldAnnotations: false +# BreakStringLiterals: true +ColumnLimit: 180 +CommentPragmas: "^ NOLINT:" +# CompactNamespaces: false +# ConstructorInitializerAllOnOneLineOrOnePerLine: true +# ConstructorInitializerIndentWidth: 4 +# ContinuationIndentWidth: 4 +# Cpp11BracedListStyle: true +# DeriveLineEnding: true +# DerivePointerAlignment: true +# DisableFormat: false +# ExperimentalAutoDetectBinPacking: false +# FixNamespaceComments: true +# ForEachMacros: +# - foreach +# - Q_FOREACH +# - BOOST_FOREACH +# IncludeBlocks: Regroup +# IncludeCategories: +# - Regex: '^' +# Priority: 2 +# SortPriority: 0 +# - Regex: '^<.*\.h>' +# Priority: 1 +# SortPriority: 0 +# - Regex: "^<.*" +# Priority: 2 +# SortPriority: 0 +# - Regex: ".*" +# Priority: 3 +# SortPriority: 0 +# IncludeIsMainRegex: "([-_](test|unittest))?$" +# IncludeIsMainSourceRegex: "" +# IndentCaseLabels: true +# IndentGotoLabels: true +# IndentPPDirectives: None +IndentWidth: 4 +# IndentWrappedFunctionNames: false +# JavaScriptQuotes: Leave +# JavaScriptWrapImports: true +# KeepEmptyLinesAtTheStartOfBlocks: false +# MacroBlockBegin: "" +# MacroBlockEnd: "" +# MaxEmptyLinesToKeep: 1 +# NamespaceIndentation: None +# ObjCBinPackProtocolList: Never +# ObjCBlockIndentWidth: 2 +# ObjCSpaceAfterProperty: false +# ObjCSpaceBeforeProtocolList: true +# PenaltyBreakAssignment: 2 +# PenaltyBreakBeforeFirstCallParameter: 1 +# PenaltyBreakComment: 300 +# PenaltyBreakFirstLessLess: 120 +# PenaltyBreakString: 1000 +# PenaltyBreakTemplateDeclaration: 10 +# PenaltyExcessCharacter: 1000000 +# PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Right +# RawStringFormats: +# - Language: Cpp +# Delimiters: +# - cc +# - CC +# - cpp +# - Cpp +# - CPP +# - "c++" +# - "C++" +# CanonicalDelimiter: "" +# BasedOnStyle: google +# - Language: TextProto +# Delimiters: +# - pb +# - PB +# - proto +# - PROTO +# EnclosingFunctions: +# - EqualsProto +# - EquivToProto +# - PARSE_PARTIAL_TEXT_PROTO +# - PARSE_TEST_PROTO +# - PARSE_TEXT_PROTO +# - ParseTextOrDie +# - ParseTextProtoOrDie +# CanonicalDelimiter: "" +# BasedOnStyle: google +# ReflowComments: true +SortIncludes: false +SortUsingDeclarations: false +# SpaceAfterCStyleCast: false +# SpaceAfterLogicalNot: false +# SpaceAfterTemplateKeyword: true +# SpaceBeforeAssignmentOperators: true +# SpaceBeforeCpp11BracedList: false +# SpaceBeforeCtorInitializerColon: true +# SpaceBeforeInheritanceColon: true +# SpaceBeforeParens: ControlStatements +# SpaceBeforeRangeBasedForLoopColon: true +# SpaceInEmptyBlock: false +# SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +# SpacesInAngles: false +# SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +# SpacesInCStyleCastParentheses: false +# SpacesInParentheses: false +# SpacesInSquareBrackets: false +# SpaceBeforeSquareBrackets: false +Standard: Cpp11 +# StatementMacros: +# - Q_UNUSED +# - QT_REQUIRE_VERSION +TabWidth: 4 +# UseCRLF: false +# UseTab: Never diff --git a/application/qfirehose/src/Android.mk b/application/qfirehose/src/Android.mk new file mode 100644 index 0000000..9c3413f --- /dev/null +++ b/application/qfirehose/src/Android.mk @@ -0,0 +1,8 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= firehose_protocol.c qfirehose.c sahara.c usb_linux.c stream_download_protocol.c md5.c usb2tcp.c +LOCAL_CFLAGS += -pie -fPIE -Wall -Wextra -Werror -O1 +LOCAL_LDFLAGS += -pie -fPIE +LOCAL_MODULE_TAGS:= optional +LOCAL_MODULE:= QFirehose +include $(BUILD_EXECUTABLE) diff --git a/application/qfirehose/src/CMakeLists.txt b/application/qfirehose/src/CMakeLists.txt new file mode 100644 index 0000000..eb63b02 --- /dev/null +++ b/application/qfirehose/src/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.4) + +project(QFirehose) +add_definitions(-Wall -Wextra -Werror -O1) + +set( QFirehose_SRCS + firehose_protocol.c qfirehose.c sahara.c usb_linux.c stream_download_protocol.c md5.c usb2tcp.c + ) + +add_executable(QFirehose ${QFirehose_SRCS}) +target_link_libraries(QFirehose PUBLIC pthread) +install (TARGETS QFirehose DESTINATION bin) diff --git a/application/qfirehose/src/Makefile b/application/qfirehose/src/Makefile new file mode 100644 index 0000000..9870bf8 --- /dev/null +++ b/application/qfirehose/src/Makefile @@ -0,0 +1,22 @@ +NDK_BUILD:=/home/aaron/share-aaron-1/android-ndk-r10e/ndk-build +SRC=firehose_protocol.c qfirehose.c sahara.c usb_linux.c stream_download_protocol.c md5.c usb2tcp.c + +CFLAGS += -Wall -Wextra -Werror -g #-s +LDFLAGS += -lpthread -ldl +BINPATH := ./out +ifeq ($(CC),cc) +CC=${CROSS_COMPILE}gcc +endif + +linux: clean + $(shell if [ -e ${BINPATH}]; then echo "file existed";else mkdir ${BINPATH}; fi;) + ${CC} ${CFLAGS} ${SRC} -o ${BINPATH}/QFirehose ${LDFLAGS} + +android: clean + rm -rf android + $(NDK_BUILD) V=0 APP_BUILD_SCRIPT=Android.mk NDK_PROJECT_PATH=`pwd` NDK_DEBUG=0 APP_ABI='armeabi-v7a,arm64-v8a' APP_PLATFORM=android-22 #16~4.1 22~5.1 25~7.1 27-32~8-12 + rm -rf obj + mv libs android + +clean: + rm -rf ${BINPATH}/QFirehose obj libs usb2tcp *~ diff --git a/application/qfirehose/src/NOTICE b/application/qfirehose/src/NOTICE new file mode 100644 index 0000000..5ab26d3 --- /dev/null +++ b/application/qfirehose/src/NOTICE @@ -0,0 +1,6 @@ +Quectel hereby grants customers of Quectel a license to use, modify, +distribute and publish the Software in binary form provided that +customers shall have no right to reverse engineer, reverse assemble, +decompile or reduce to source code form any portion of the Software. +Under no circumstances may customers modify, demonstrate, use, deliver +or disclose any portion of the Software in source code form. \ No newline at end of file diff --git a/application/qfirehose/src/README.md b/application/qfirehose/src/README.md new file mode 100644 index 0000000..d9d23fb --- /dev/null +++ b/application/qfirehose/src/README.md @@ -0,0 +1,108 @@ +# QFIREHOSE 升级进度通知机制说明 + +> ​ QFirehose 目前支持两种进度通知方式。基于文件的通知方式,基于消息队列的通知机制。两种方式均可使用。Android设备由于系统限制,system V IPC 机制支持不完善,不能支持消息队列的方式获取进度。 +> +> ​ 其中基于文件的形式很容易替换成,有名管道的形式,只需把创建并打开改成创建并打开管道的形式(参考`man mkfifio`)。「注:管道需要先以读方式打开,再以写方式打开,否则会有错误」 + +## 一、进度读写说明 +> QFirehose 进度都是以**整数**形式写到文件或者消息队列里。 + +1. 在升级开始阶段写入进度**0**预示这升级开始。 +2. 之后每次进度有变化都会重新更新进度信息。 +3. 升级完成最后结果是**100**。 +4. 升级发生异常会写入进度 **-1**. 「注:特殊情况,如程序异常崩溃进度信息可能来不及写入而是空的,需要第三方程序考虑文件内容为空的情况」 + +## 二、文件形式的通知机制 +### A. 文件读写说明 + +​ 文件方式的交互文件路径为: Android **“/data/update.conf”**, 其他 **“/tmp/update.conf”**。(修改QFirehose 源码可以修改这个文件)。另外需要保证/data 或者/tmp 这个目录存在,且QFirehose有读写、创建文件的权限。 +​ 由于文件是持续存在磁盘上的。为了保证命令行方式的cat 读与标准API 的read 两种方式的可读性。**进度写方式为覆盖写,意思就是新的进度会覆盖掉之前的内容。**表现在实现上就是,每次写之前移动文件指针到文件头,然后写入进度。读进度时候需要注意这一点!!! +​ 另外注意的是QFirehose 不负责文件的删除,如果需要删除需要由第三方程序在升级结束/出错删除进度文件! + +### B.使用说明 +​ QFirehose 默认不使能通知功能。如需使能文件方式通知逻辑需要在编译时指定宏 **USE_IPC_FILE** , 建议在Makefile里用 **-DUSE_IPC_FILE**来使能. 如下代码所示 + +```makefile +NDK_BUILD:=/workspace/android-ndk/android-ndk-r10e/ndk-build + +ifeq ($(CC),cc) +CC=${CROSS_COMPILE}gcc +endif + +cflags += -Wno-format-overflow + +linux: clean + ${CC} -g -Wall -DUSE_IPC_FILE -s ${cflags} firehose_protocol.c qfirehose.c sahara_protocol.c usb_linux.c stream_download_protocol.c md5.c usb2tcp.c -o QFirehose -lpthread -ldl + +clean: + rm -rf QFirehose obj libs android usb2tcp *~ + +``` + +## 三、消息队列的通知机制 +### A. 原理说明 +System V 方式消息队列。QFirehose 负责创建消息队列,写入进度。**不负责删除消息,不负责删除消息队列。** +如下图,QFirehose 定义的消息结构体,消息类型,与message key 获取机制。第三方应用需要与QFirehose 保持一致。如需修改,可以修改QFirehose 源码。 + +System V message queue: + +```c +#define MSGBUFFSZ 16 +struct message +{ + long mtype; + char mtext[MSGBUFFSZ]; +}; + +#define MSG_FILE "/etc/passwd" +#define MSG_TYPE_IPC 1 +``` + +并同时修改编译脚本/源码,定义宏 **USE_IPC_MSG**, 如下所示 + +```makefile +NDK_BUILD:=/workspace/android-ndk/android-ndk-r10e/ndk-build + +ifeq ($(CC),cc) +CC=${CROSS_COMPILE}gcc +endif + +cflags += -Wno-format-overflow + +linux: clean + ${CC} -g -Wall -DUSE_IPC_MSG -s ${cflags} firehose_protocol.c qfirehose.c sahara_protocol.c usb_linux.c stream_download_protocol.c md5.c usb2tcp.c -o QFirehose -lpthread -ldl + +clean: + rm -rf QFirehose obj libs android usb2tcp *~ +``` + +QFirehose 实现了四个操作函数:消息队列的创建,删除,写,读。但是只用到了创建与读函数。另外两个仅供参考与测试用。 +### B.使用说明 +QFirehose 负责创建消息队列,写入进度。不会删除消息以及消息队列。建议第三方应用在升级程序退出之后主动删除消息队列。 + +QFirehose 支持对消息队列的测试,这需要启用宏 **IPC_TEST** 。开启这个宏之后,QFirehose 会在写入消息后再次读取消息,并打印到`STDOUT`,检查打印可知是否正确。 + +```c +/** + * this function will not delete the msg queue + */ +int update_progress_msg(int percent) +{ + char buff[MSGBUFFSZ]; + int msgid = msg_get(); + if (msgid < 0) + return -1; + snprintf(buff, sizeof(buff), "%d", percent); + +#ifndef IPC_TEST + return msg_send(msgid, MSG_TYPE_IPC, buff); +#else + msg_send(msgid, MSG_TYPE_IPC, buff); + struct message info; + info.mtype = MSG_TYPE_IPC; + msg_recv(msgid, &info); + printf("msg queue read: %s\n", info.mtext); +#endif +} +``` + diff --git a/application/qfirehose/src/ReleaseNote.txt b/application/qfirehose/src/ReleaseNote.txt new file mode 100644 index 0000000..8e85077 --- /dev/null +++ b/application/qfirehose/src/ReleaseNote.txt @@ -0,0 +1,198 @@ +Release Notes +[QFirehose_Linux_Android_V1.4.21] +Date: 08/14/2024 +enhancement: + 1. Support EG120KEABA upgrade in RNDIS mode +fix: + +[QFirehose_Linux_Android_V1.4.20] +Date: 07/18/2024 +enhancement: + 1. Support AG600K to upgrade using adb command let module into edl + 2. Support sec-boot upgrade file to burn +fix: + +Release Notes +[QFirehose_Linux_Android_V1.4.19] +Date: 04/30/2024 +enhancement: + 1. The input parameter can be a precise file to specify the reading of the upgrade file. + 2. RG650V using rndis can use this tool to upgrade.(./QFirehose -f ${upgrade_file_path} -d emmc) +fix: + +[QFirehose_Linux_Android_V1.4.18] +Date: 12/29/2023 +enhancement: + 1. New support for upgrading the laptop module EM120K-GL + 2. New support for SA885GAPNA(ufs) upgrade + 3. Added support for AG215SGLBA(with a single image greater than 40M in the firmware package) upgrade + 4. New support for RM520NGL(custom PID is 0x0804) upgrade +fix: + +[QFirehose_Linux_Android_V1.4.17] +Date: 09/13/2023 +enhancement: + 1. Added support for upgrading using zip and 7z package + 2. Optimize pcie code + 3. Optimize sahara code +fix: + +[QFirehose_Linux_Android_V1.4.16] +Date: 08/08/2023 +enhancement: + 1. Update copyright + 2. Hide -e full wipe, but retain this feature + 3. Fix errors in klocwork scanning + 4. Code optimization for erase partitioning operations for nand storage modules +fix: + +[QFirehose_Linux_Android_V1.4.15] +Date: 06/29/2023 +enhancement: + 1. Added support for upgrading laptop EM05G-SE10 + 2. Modify the copyright time and add copyright to some c or h files + 3. Optimize sahara protocol code +fix: + +[QFirehose_Linux_Android_V1.4.14] +Date: 04/29/2023 +enhancement: + 1. Added support for upgrading laptop EM05-G and EM061KGL + 2. Added support for AG590ECNAB upgrade + 3. Added support for upgrading the RM520NGL Lenovo 5G module (with a specified PID of 0x0803) + 4. Added support for upgrading AG215S-GLR signed firmware version packs +fix: + +[QFirehose_Linux_Android_V1.4.13] +Date: 02/20/2023 +enhancement: + 1. New support for upgrading multiple PCIE modules in a specified way, with a maximum of 10 PCIE modules connected at the same time + 2. Optimization module connection USB3.0 upgrade +fix: + +[QFirehose_Linux_Android_V1.4.12] +Date: 12/15/2022 +enhancement: + 1. Added support for Qualcomm subcontracting firmware upgrade (also compatible with non subcontracting firmware) + 2. Added EC20��3C93 FFFF��upgrade + 3. Add BG95M3 anti misburning function + 4. Add other device type full wipe upgrade functions except nand +fix: + +[QFirehose_Linux_Android_V1.4.11] +Date: 08/29/2022 +enhancement: + 1. Better support emmc and ufs smart module + 2. remove function upgrade one file by '-f filename', nobody use it + and make source code complex and difficult to understand. + if customer need this function, can by modify firehose/rawprogram_nand_p4K_b256K_factory.xml + 3. if there is no md5.txt, just ignore, donot treat as error + 4. New support for upgrading Qualcomm AG215SGLR module and AG215SGLBA module +fix: + +[QFirehose_Linux_Android_V1.4.10] +Date: 07/15/2022 +enhancement: + 1. New support for laptop EM05 series and EG060K-EA module upgrade +fix: + +[QFirehose_Linux_Android_V1.4.9] +Date: 01/17/2022 +enhancement: + 1. Solve the problem that DM upgrade is stuck because DM keeps spitting logs +fix: + +[QFirehose_Linux_Android_V1.4.8] +Date: 11/12/2021 +enhancement: + 1. EM05CE Interface0 is a non-DM port with input and output endpoints. The actual DM port is Interface3 + 2. EM05-G Upgrade Process Power off and power on. The next upgrade takes a long time to switch to download mode. Increase the waiting time to 10 seconds + 3. Optimized receiving and sending buf, no longer sharing one buf +fix: + +[QFirehose_Linux_Android_V1.4.7] +Date: 9/28/2021 +enhancement: + 1. Support EM05-G, DM is at usb interface 3 + 2. Added support for smart platform SC600Y-EM and SC60-CE upgrade + 3. Support sdx20 quallcomm default 056c/901f +fix: + +[Quectel_LTE&5G_QFirehose_Linux&Android_V1.4.6] +Date: 6/11/2021 +enhancement: +fix: + 1. ���Ӷ�EC20������Ŀ + +[Quectel_LTE&5G_QFirehose_Linux&Android_V1.4.5] +Date: 3/4/2021 +enhancement: +fix: + 1. fix upgrdae fail of AG550/EM120 + +[Quectel_LTE&5G_QFirehose_Linux&Android_V1.4.4] +Date: 1/27/202 +enhancement: +fix: + 1. increase timeout of erase cmd to 15 seconds (from 6 seconds) + +[Quectel_LTE&5G_QFirehose_Linux&Android_V1.4.3] +Date: 12/2/2020 +enhancement: + 1. '-u usbmon.log': The time stamp in usbmon.log is same QFirehose self + 2. USB ZLP: if kernel verison <= 4.2 and usb controoler type is XHCI. + there is a USB ZLP bug which cause FW upgrade fail at 0%, + print Warnning for this bug and tell customer how to fix. +fix: + 1. AG35: FW upgrade fail (A very low probability) in one customer's factory + +[Quectel_LTE&5G_QFirehose_Linux&Android_V1.4] +Date: 8/26/2020 +enhancement: + 1. release to +fix: + +[Quectel_LTE&5G_QFirehose_Linux&Android_V1.3.3] +Date: 7/27/2020 +enhancement: +fix: + 1. AG525 has more than 32 partitions, support this + +[Quectel_LTE&5G_QFirehose_Linux&Android_V1.3.2] +Date: 7/27/2020 +enhancement: +fix: + 1. filter out ASR/HISI modems, only support MDM/SDX modems + 2. FW upgrade fail when USB3.0 modems switch EDL mode (05c6:9008), + will become usb2.0 device, and the /sys/bus/usb/devices/x-x will change + +[Quectel_LTE&5G_QFirehose_Linux&Android_V1.3] +Date: 4/14/2020 +enhancement: + 1. X55 support upgrade from RAM DUMP state +fix: + +[LTE&5G_QFirehose_Linux&Android_V1.2.4] +Date: 3/26/2020 +enhancement: + 1. make sure 'first erase SBL, last program SBL', even The sequence in update/firehose/rawprogram*.xml is wrong +fix: + +[LTE&5G_QFirehose_Linux&Android_V1.2.3] +Date: 2/24/2019 +enhancement: + 1. support 'vendor' attribute in rawprogram*.xml + 2. support '-u usbmon_logfile', can capture usbmon log when upgrade module. +fix: + +[LTE&5G_QFirehose_Linux&Android_V1.2.2] +Date: 12/11/2019 +enhancement: + 1. auto detect and detach kernel driver when modem in EDL mode (05c6:9008). +fix: + 1. Do not exit if get version command fails + +[LTE&5G_QFirehose_Linux&Android_V1.2] +Date: 2019/07/31 +enhancement: + 1. First Release Version of QFirehose Tool. diff --git a/application/qfirehose/src/android/arm64-v8a/QFirehose b/application/qfirehose/src/android/arm64-v8a/QFirehose new file mode 100644 index 0000000..98276b6 Binary files /dev/null and b/application/qfirehose/src/android/arm64-v8a/QFirehose differ diff --git a/application/qfirehose/src/android/armeabi-v7a/QFirehose b/application/qfirehose/src/android/armeabi-v7a/QFirehose new file mode 100644 index 0000000..22ffa7b Binary files /dev/null and b/application/qfirehose/src/android/armeabi-v7a/QFirehose differ diff --git a/application/qfirehose/src/firehose_protocol.c b/application/qfirehose/src/firehose_protocol.c new file mode 100644 index 0000000..f6fb277 --- /dev/null +++ b/application/qfirehose/src/firehose_protocol.c @@ -0,0 +1,2297 @@ +/* + Copyright 2023 Quectel Wireless Solutions Co.,Ltd + + Quectel hereby grants customers of Quectel a license to use, modify, + distribute and publish the Software in binary form provided that + customers shall have no right to reverse engineer, reverse assemble, + decompile or reduce to source code form any portion of the Software. + Under no circumstances may customers modify, demonstrate, use, deliver + or disclose any portion of the Software in source code form. +*/ + +#include "usb_linux.h" +#include +#include +#include +/* +#define error_return() \ + do \ + { \ + dbg_time("%s %s %d fail\n", __FILE__, __func__, __LINE__); \ + return __LINE__; \ + } while (0) + */ +int recv_sc600y_configure_num = 1; +extern const char *q_device_type; +static int fh_recv_cmd_sk[2]; +extern unsigned q_module_packet_sign; + +extern unsigned q_erase_all_before_download; +extern int update_transfer_bytes(long long bytes_cur); +extern int show_progress(); + +char file_name_image[128] = {0}; +char file_name_image_dir[256] = {0}; + +typedef struct sparse_header +{ + uint32_t magic; /* 0xed26ff3a */ + uint16_t major_version; /* (0x1) - reject images with higher major versions */ + uint16_t minor_version; /* (0x0) - allow images with higer minor versions */ + uint16_t file_hdr_sz; /* 28 bytes for first revision of the file format */ + uint16_t chunk_hdr_sz; /* 12 bytes for first revision of the file format */ + uint32_t blk_sz; /* block size in bytes, must be a multiple of 4 (4096) */ + uint32_t total_blks; /* total blocks in the non-sparse output image */ + uint32_t total_chunks; /* total chunks in the sparse input image */ + uint32_t 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 + +typedef struct chunk_header +{ + uint16_t chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */ + uint16_t reserved1; + uint32_t chunk_sz; /* in blocks in output image */ + uint32_t total_sz; /* in bytes of chunk input file including chunk header and + data */ +} chunk_header_t; + +typedef struct chunk_polymerization_params +{ + uint32_t total_chunk_sz; + uint32_t total_sz; + uint16_t total_chunk_count; + // uint16_t file_sector_offset; +} chunk_polymerization_param; + +typedef struct SparseImgParams +{ + chunk_polymerization_param chunk_polymerization_data[100]; + chunk_polymerization_param chunk_polymerization_cac3[100]; + uint16_t total_count; + uint16_t total_cac3_count; + uint16_t file_first_sector_offset; //��һ����ͷ��ȡ�����涼���Լ������ +} SparseImgParam; + +SparseImgParam SparseImgData; + +struct fh_configure_cmd +{ + const char *type; + const char *MemoryName; + uint32_t Verbose; + uint32_t AlwaysValidate; + uint32_t MaxDigestTableSizeInBytes; + uint32_t MaxPayloadSizeToTargetInBytes; + uint32_t MaxPayloadSizeFromTargetInBytes; // 2048 + uint32_t MaxPayloadSizeToTargetInByteSupported; // 16k + uint32_t ZlpAwareHost; + uint32_t SkipStorageInit; +}; + +struct fh_erase_cmd +{ + const char *type; + // uint32_t PAGES_PER_BLOCK; + uint32_t SECTOR_SIZE_IN_BYTES; + // char label[32]; + uint32_t last_sector; + uint32_t num_partition_sectors; + // uint32_t physical_partition_number; + uint32_t start_sector; +}; + +struct fh_program_cmd +{ + const char *type; + char *filename; + char *sparse; + uint32_t filesz; + // uint32_t PAGES_PER_BLOCK; + uint32_t SECTOR_SIZE_IN_BYTES; + // char label[32]; + // uint32_t last_sector; + uint32_t num_partition_sectors; + uint32_t physical_partition_number; + uint32_t start_sector; + uint32_t file_sector_offset; + uint32_t UNSPARSE_FILE_SIZE; + // char sparse[16]; +}; + +struct fh_response_cmd +{ + const char *type; + const char *value; + uint32_t rawmode; + uint32_t MaxPayloadSizeToTargetInBytes; +}; + +struct fh_log_cmd +{ + const char *type; +}; + +struct fh_patch_cmd +{ + const char *type; + char *filename; + uint32_t filesz; + uint32_t SECTOR_SIZE_IN_BYTES; + uint32_t num_partition_sectors; +}; + +struct fh_cmd_header +{ + const char *type; +}; + +struct fh_vendor_defines +{ + const char *type; // "vendor" +}; + +struct fh_cmd +{ + union + { + struct fh_cmd_header cmd; + struct fh_configure_cmd cfg; + struct fh_erase_cmd erase; + struct fh_program_cmd program; + struct fh_response_cmd response; + struct fh_log_cmd log; + struct fh_patch_cmd patch; + struct fh_vendor_defines vdef; + }; + int part_upgrade; + char xml_original_data[512]; +}; + +#define fh_cmd_num 1024 // AG525 have more than 64 partition +struct fh_data +{ + const char *firehose_dir; + const void *usb_handle; + unsigned MaxPayloadSizeToTargetInBytes; + unsigned fh_cmd_count; + unsigned fh_patch_count; + unsigned ZlpAwareHost; + struct fh_cmd fh_cmd_table[fh_cmd_num]; + + unsigned xml_tx_size; + unsigned xml_rx_size; + char xml_tx_buf[1024]; + char xml_rx_buf[1024]; +}; + +static const char *fh_xml_find_value(const char *xml_line, const char *key, char **ppend) +{ + char *pchar = strstr(xml_line, key); + char *pend; + + if (!pchar) + { + if (strcmp(key, "sparse")) dbg_time("%s: no key %s in %s\n", __func__, key, xml_line); + return NULL; + } + + pchar += strlen(key); + if (pchar[0] != '=' && pchar[1] != '"') + { + dbg_time("%s: no start %s in %s\n", __func__, "=\"", xml_line); + return NULL; + } + + pchar += strlen("=\""); + pend = strstr(pchar, "\""); + if (!pend) + { + dbg_time("%s: no end %s in %s\n", __func__, "\"", xml_line); + return NULL; + } + + *ppend = pend; + return pchar; +} + +static const char *fh_xml_get_value(const char *xml_line, const char *key) +{ + static char value[64]; + char *pend; + const char *pchar = fh_xml_find_value(xml_line, key, &pend); + + if (!pchar) + { + return NULL; + } + + int len = pend - pchar; + if (len >= 64) return NULL; + + strncpy(value, pchar, pend - pchar); + value[pend - pchar] = '\0'; + + return value; +} + +static void fh_xml_set_value(char *xml_line, const char *key, unsigned value) +{ + char *pend; + const char *pchar = fh_xml_find_value(xml_line, key, &pend); + char value_str[32]; + char *tmp_line = malloc(strlen(xml_line) + 1 + sizeof(value_str)); + + if (!pchar || !tmp_line) + { + if (tmp_line) + { + free(tmp_line); + tmp_line = NULL; + } + return; + } + + strcpy(tmp_line, xml_line); + + snprintf(value_str, sizeof(value_str), "%u", value); + tmp_line[pchar - xml_line] = '\0'; + strcat(tmp_line, value_str); + strcat(tmp_line, pend); + + strcpy(xml_line, tmp_line); + free(tmp_line); +} + +static int fh_parse_xml_line(const char *xml_line, struct fh_cmd *fh_cmd) +{ + const char *pchar = NULL; + size_t len = strlen(xml_line); + + memset(fh_cmd, 0, sizeof(struct fh_cmd)); + strncpy(fh_cmd->xml_original_data, xml_line, 512); + if (fh_cmd->xml_original_data[len - 1] == '\n') fh_cmd->xml_original_data[len - 1] = '\0'; + + if (strstr(xml_line, "vendor=\"quectel\"")) + { + fh_cmd->vdef.type = "vendor"; + return 0; + } + else if (!strncmp(xml_line, "erase.type = "erase"; + if (strstr(xml_line, "last_sector")) + { + if ((pchar = fh_xml_get_value(xml_line, "last_sector"))) fh_cmd->erase.last_sector = atoi(pchar); + } + if ((pchar = fh_xml_get_value(xml_line, "start_sector"))) fh_cmd->erase.start_sector = atoi(pchar); + if ((pchar = fh_xml_get_value(xml_line, "num_partition_sectors"))) fh_cmd->erase.num_partition_sectors = atoi(pchar); + if ((pchar = fh_xml_get_value(xml_line, "SECTOR_SIZE_IN_BYTES"))) fh_cmd->erase.SECTOR_SIZE_IN_BYTES = atoi(pchar); + + return 0; + } + else if (!strncmp(xml_line, "program.type = "program"; + if ((pchar = fh_xml_get_value(xml_line, "filename"))) + { + fh_cmd->program.filename = strdup(pchar); + if (fh_cmd->program.filename[0] == '\0') + { // some fw version have blank program line, ignore it. + return -1; + } + } + + if ((pchar = fh_xml_get_value(xml_line, "sparse"))) + { + fh_cmd->program.sparse = strdup(pchar); + } + else + fh_cmd->program.sparse = NULL; + + if ((pchar = fh_xml_get_value(xml_line, "start_sector"))) fh_cmd->program.start_sector = atoi(pchar); + if ((pchar = fh_xml_get_value(xml_line, "num_partition_sectors"))) fh_cmd->program.num_partition_sectors = atoi(pchar); + if ((pchar = fh_xml_get_value(xml_line, "SECTOR_SIZE_IN_BYTES"))) fh_cmd->program.SECTOR_SIZE_IN_BYTES = atoi(pchar); + + if (fh_cmd->program.sparse != NULL && !strncasecmp(fh_cmd->program.sparse, "true", 4)) + { + if ((pchar = fh_xml_get_value(xml_line, "file_sector_offset"))) fh_cmd->program.file_sector_offset = atoi(pchar); + if ((pchar = fh_xml_get_value(xml_line, "physical_partition_number"))) fh_cmd->program.physical_partition_number = atoi(pchar); + } + + return 0; + } + else if (!strncmp(xml_line, "patch.type = "patch"; + pchar = fh_xml_get_value(xml_line, "filename"); + if (pchar && strcmp(pchar, "DISK")) return -1; + return 0; + } + else if (!strncmp(xml_line, "response.type = "response"; + pchar = fh_xml_get_value(xml_line, "value"); + if (pchar) + { + if (!strcmp(pchar, "ACK")) + fh_cmd->response.value = "ACK"; + else if (!strcmp(pchar, "NAK")) + fh_cmd->response.value = "NAK"; + else + fh_cmd->response.value = "OTHER"; + } + if (strstr(xml_line, "rawmode")) + { + pchar = fh_xml_get_value(xml_line, "rawmode"); + if (pchar) + { + fh_cmd->response.rawmode = !strcmp(pchar, "true"); + } + } + else if (strstr(xml_line, "MaxPayloadSizeToTargetInBytes")) + { + pchar = fh_xml_get_value(xml_line, "MaxPayloadSizeToTargetInBytes"); + if (pchar) + { + fh_cmd->response.MaxPayloadSizeToTargetInBytes = atoi(pchar); + } + } + return 0; + } + else if (!strncmp(xml_line, "program.type = "log"; + return 0; + } + + error_return(); +} + +static int fh_parse_xml_file(struct fh_data *fh_data, const char *xml_file) +{ + FILE *fp = fopen(xml_file, "rb"); + + if (fp == NULL) + { + dbg_time("%s fail to fopen(%s), errno: %d (%s)\n", __func__, xml_file, errno, strerror(errno)); + error_return(); + } + + while (fgets(fh_data->xml_tx_buf, fh_data->xml_tx_size, fp)) + { + char *xml_line = strstr(fh_data->xml_tx_buf, "<"); + char *c_start = NULL; + + if (!xml_line) continue; + + c_start = strstr(xml_line, ""); + + if (c_end) + { + /* + + + + */ + char *tmp = strstr(xml_line, "/>"); + if (tmp && (tmp < c_start || tmp > c_end)) + { + memset(c_start, ' ', c_end - c_start + strlen("-->")); + goto __fh_parse_xml_line; + } + + continue; + } + else + { + /* + + --> + */ + do + { + if (fgets(fh_data->xml_tx_buf, fh_data->xml_tx_size, fp) == NULL) + { + break; + }; + xml_line = fh_data->xml_tx_buf; + } while (!strstr(xml_line, "-->") && strstr(xml_line, " SAHARA_HELLO_RESPONSE +[004.052]: STATE <-- SAHARA_WAIT_COMMAND +[004.053]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.053]: 0x0000000d 0x00000000 0x00000034 +[004.056]: STATE <-- SAHARA_WAIT_COMMAND +[004.056]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.056]: 0x0000000d 0x00000034 0x00000080 +[004.056]: STATE <-- SAHARA_WAIT_COMMAND +[004.057]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.057]: 0x0000000d 0x00001000 0x00001000 +[004.058]: STATE <-- SAHARA_WAIT_COMMAND +[004.087]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.087]: 0x0000000d 0x00002000 0x000009d0 +[004.087]: STATE <-- SAHARA_WAIT_COMMAND +[004.088]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.088]: 0x0000000d 0x00003000 0x00001000 +[004.093]: STATE <-- SAHARA_WAIT_COMMAND +[004.094]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.094]: 0x0000000d 0x00004000 0x00001000 +[004.094]: STATE <-- SAHARA_WAIT_COMMAND +[004.095]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.095]: 0x0000000d 0x00005000 0x00001000 +[004.095]: STATE <-- SAHARA_WAIT_COMMAND +[004.096]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.096]: 0x0000000d 0x00006000 0x00001000 +[004.096]: STATE <-- SAHARA_WAIT_COMMAND +[004.097]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.097]: 0x0000000d 0x00007000 0x00001000 +[004.098]: STATE <-- SAHARA_WAIT_COMMAND +[004.098]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.098]: 0x0000000d 0x00008000 0x00001000 +[004.099]: STATE <-- SAHARA_WAIT_COMMAND +[004.099]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.099]: 0x0000000d 0x00009000 0x00001000 +[004.100]: STATE <-- SAHARA_WAIT_COMMAND +[004.100]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.100]: 0x0000000d 0x0000a000 0x00001000 +[004.101]: STATE <-- SAHARA_WAIT_COMMAND +[004.101]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.101]: 0x0000000d 0x0000b000 0x00001000 +[004.103]: STATE <-- SAHARA_WAIT_COMMAND +[004.103]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.103]: 0x0000000d 0x0000c000 0x00001000 +[004.104]: STATE <-- SAHARA_WAIT_COMMAND +[004.104]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.104]: 0x0000000d 0x0000d000 0x00001000 +[004.105]: STATE <-- SAHARA_WAIT_COMMAND +[004.105]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.105]: 0x0000000d 0x0000e000 0x00001000 +[004.106]: STATE <-- SAHARA_WAIT_COMMAND +[004.106]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.106]: 0x0000000d 0x0000f000 0x00001000 +[004.107]: STATE <-- SAHARA_WAIT_COMMAND +[004.107]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.108]: 0x0000000d 0x00010000 0x00001000 +[004.108]: STATE <-- SAHARA_WAIT_COMMAND +[004.108]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.109]: 0x0000000d 0x00011000 0x00001000 +[004.109]: STATE <-- SAHARA_WAIT_COMMAND +[004.110]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.110]: 0x0000000d 0x00012000 0x00001000 +[004.110]: STATE <-- SAHARA_WAIT_COMMAND +[004.111]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.111]: 0x0000000d 0x00013000 0x00001000 +[004.111]: STATE <-- SAHARA_WAIT_COMMAND +[004.112]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.112]: 0x0000000d 0x00014000 0x00001000 +[004.113]: STATE <-- SAHARA_WAIT_COMMAND +[004.113]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.113]: 0x0000000d 0x00015000 0x00001000 +[004.114]: STATE <-- SAHARA_WAIT_COMMAND +[004.114]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.114]: 0x0000000d 0x00016000 0x00001000 +[004.115]: STATE <-- SAHARA_WAIT_COMMAND +[004.115]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.115]: 0x0000000d 0x00017000 0x00001000 +[004.116]: STATE <-- SAHARA_WAIT_COMMAND +[004.116]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.116]: 0x0000000d 0x00018000 0x00001000 +[004.117]: STATE <-- SAHARA_WAIT_COMMAND +[004.117]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.117]: 0x0000000d 0x00019000 0x00001000 +[004.118]: STATE <-- SAHARA_WAIT_COMMAND +[004.118]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.118]: 0x0000000d 0x0001a000 0x00001000 +[004.119]: STATE <-- SAHARA_WAIT_COMMAND +[004.119]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.119]: 0x0000000d 0x0001b000 0x00001000 +[004.120]: STATE <-- SAHARA_WAIT_COMMAND +[004.120]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.120]: 0x0000000d 0x0001c000 0x00001000 +[004.121]: STATE <-- SAHARA_WAIT_COMMAND +[004.121]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.122]: 0x0000000d 0x0001d000 0x00001000 +[004.122]: STATE <-- SAHARA_WAIT_COMMAND +[004.123]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.123]: 0x0000000d 0x0001e000 0x00001000 +[004.123]: STATE <-- SAHARA_WAIT_COMMAND +[004.124]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.124]: 0x0000000d 0x0001f000 0x00001000 +[004.124]: STATE <-- SAHARA_WAIT_COMMAND +[004.125]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.125]: 0x0000000d 0x00020000 0x00001000 +[004.125]: STATE <-- SAHARA_WAIT_COMMAND +[004.126]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.126]: 0x0000000d 0x00021000 0x00001000 +[004.126]: STATE <-- SAHARA_WAIT_COMMAND +[004.127]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.127]: 0x0000000d 0x00022000 0x00001000 +[004.127]: STATE <-- SAHARA_WAIT_COMMAND +[004.128]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.128]: 0x0000000d 0x00023000 0x00001000 +[004.128]: STATE <-- SAHARA_WAIT_COMMAND +[004.129]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.129]: 0x0000000d 0x00024000 0x00001000 +[004.129]: STATE <-- SAHARA_WAIT_COMMAND +[004.130]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.130]: 0x0000000d 0x00025000 0x00001000 +[004.130]: STATE <-- SAHARA_WAIT_COMMAND +[004.131]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.131]: 0x0000000d 0x00026000 0x00001000 +[004.131]: STATE <-- SAHARA_WAIT_COMMAND +[004.132]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.132]: 0x0000000d 0x00027000 0x00001000 +[004.132]: STATE <-- SAHARA_WAIT_COMMAND +[004.133]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.133]: 0x0000000d 0x00028000 0x00001000 +[004.133]: STATE <-- SAHARA_WAIT_COMMAND +[004.134]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.134]: 0x0000000d 0x00029000 0x00001000 +[004.134]: STATE <-- SAHARA_WAIT_COMMAND +[004.135]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.135]: 0x0000000d 0x0002a000 0x00001000 +[004.135]: STATE <-- SAHARA_WAIT_COMMAND +[004.136]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.136]: 0x0000000d 0x0002b000 0x00000578 +[004.136]: STATE <-- SAHARA_WAIT_COMMAND +[004.136]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.137]: 0x0000000d 0x0002b580 0x00001000 +[004.137]: STATE <-- SAHARA_WAIT_COMMAND +[004.137]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.138]: 0x0000000d 0x0002c580 0x00001000 +[004.138]: STATE <-- SAHARA_WAIT_COMMAND +[004.138]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.139]: 0x0000000d 0x0002d580 0x00001000 +[004.139]: STATE <-- SAHARA_WAIT_COMMAND +[004.139]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.140]: 0x0000000d 0x0002e580 0x00001000 +[004.140]: STATE <-- SAHARA_WAIT_COMMAND +[004.140]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.141]: 0x0000000d 0x0002f580 0x00001000 +[004.141]: STATE <-- SAHARA_WAIT_COMMAND +[004.141]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.142]: 0x0000000d 0x00030580 0x00001000 +[004.142]: STATE <-- SAHARA_WAIT_COMMAND +[004.143]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.143]: 0x0000000d 0x00031580 0x00001000 +[004.143]: STATE <-- SAHARA_WAIT_COMMAND +[004.144]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.144]: 0x0000000d 0x00032580 0x00001000 +[004.144]: STATE <-- SAHARA_WAIT_COMMAND +[004.145]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.145]: 0x0000000d 0x00033580 0x00001000 +[004.145]: STATE <-- SAHARA_WAIT_COMMAND +[004.146]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.146]: 0x0000000d 0x00034580 0x00001000 +[004.146]: STATE <-- SAHARA_WAIT_COMMAND +[004.147]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.147]: 0x0000000d 0x00035580 0x00001000 +[004.147]: STATE <-- SAHARA_WAIT_COMMAND +[004.148]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.148]: 0x0000000d 0x00036580 0x00001000 +[004.148]: STATE <-- SAHARA_WAIT_COMMAND +[004.149]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.149]: 0x0000000d 0x00037580 0x00001000 +[004.149]: STATE <-- SAHARA_WAIT_COMMAND +[004.150]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.150]: 0x0000000d 0x00038580 0x00001000 +[004.150]: STATE <-- SAHARA_WAIT_COMMAND +[004.151]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.151]: 0x0000000d 0x00039580 0x00001000 +[004.151]: STATE <-- SAHARA_WAIT_COMMAND +[004.152]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.152]: 0x0000000d 0x0003a580 0x00001000 +[004.152]: STATE <-- SAHARA_WAIT_COMMAND +[004.153]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.153]: 0x0000000d 0x0003b580 0x00001000 +[004.153]: STATE <-- SAHARA_WAIT_COMMAND +[004.154]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.154]: 0x0000000d 0x0003c580 0x00001000 +[004.154]: STATE <-- SAHARA_WAIT_COMMAND +[004.155]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.155]: 0x0000000d 0x0003d580 0x00001000 +[004.155]: STATE <-- SAHARA_WAIT_COMMAND +[004.156]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.156]: 0x0000000d 0x0003e580 0x00001000 +[004.156]: STATE <-- SAHARA_WAIT_COMMAND +[004.157]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.157]: 0x0000000d 0x0003f580 0x00001000 +[004.157]: STATE <-- SAHARA_WAIT_COMMAND +[004.158]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.158]: 0x0000000d 0x00040580 0x00001000 +[004.158]: STATE <-- SAHARA_WAIT_COMMAND +[004.159]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.159]: 0x0000000d 0x00041580 0x00001000 +[004.159]: STATE <-- SAHARA_WAIT_COMMAND +[004.160]: RECEIVED <-- SAHARA_READ_DATA_ID +[004.160]: 0x0000000d 0x00042580 0x00000800 +[004.160]: STATE <-- SAHARA_WAIT_COMMAND +[004.163]: RECEIVED <-- SAHARA_END_IMAGE_TX_ID +[004.163]: image_id = 13, status = 0 +[004.164]: SENDING --> SAHARA_DONE +[004.164]: STATE <-- SAHARA_WAIT_DONE_RESP +[004.164]: RECEIVED <-- SAHARA_DONE_RESP_ID +[004.164]: image_tx_status = 1 +[004.164]: Successfully uploaded all images +[004.164]: Sahara protocol completed +[004.165]: dir=/mnt/RM500QGLAAR01A01M4G_BETA_20200428F_factory/update/firehose +[004.165]: d_name=rawprogram_nand_p4K_b256K_factory.xml +[005.185]: +[005.185]: +[005.185]: +[005.186]: +[005.186]: +[005.187]: +[005.187]: +[005.187]: +[005.188]: +[005.188]: +[005.188]: +[005.188]: +[005.189]: +[005.189]: +[005.189]: +[005.190]: +[005.190]: +[005.190]: +[005.191]: +[005.191]: +[006.182]: inf[0] ep_in -1/1024, errno = 110 (Operation timed out), timeout=1000 +[006.182]: qusb_noblock_read read=0, errno: 110 (Operation timed out) +[006.182]: qusb_noblock_read cur=0, min_size=1 +[006.182]: firehose_protocol.c fh_recv_cmd 327 fail +[006.183]: +[006.184]: +[006.184]: +[006.186]: +[006.186]: +[006.187]: +[006.256]: +[006.256]: +[006.645]: +[006.653]: +[006.654]: +[006.656]: +[006.657]: +[006.658]: +[006.660]: +[006.661]: +[006.662]: +[006.663]: +[006.665]: +[006.807]: +[006.807]: +[006.808]: +[006.810]: +[006.811]: +[006.818]: +[006.819]: +[006.820]: +[006.821]: +[006.822]: +[006.825]: +[006.826]: +[006.827]: +[006.828]: +[006.829]: +[006.832]: +[006.833]: +[006.834]: +[006.835]: +[006.835]: +[006.839]: +[006.840]: +[006.841]: +[006.842]: +[006.843]: +[006.846]: +[006.847]: +[006.848]: +[006.848]: +[006.849]: +[006.853]: +[006.854]: +[006.855]: +[006.856]: +[006.856]: +[006.860]: +[006.861]: +[006.862]: +[006.863]: +[006.864]: +[006.867]: +[006.868]: +[006.869]: +[006.870]: +[006.871]: +[006.880]: +[006.880]: +[006.881]: +[006.882]: +[006.883]: +[006.887]: +[006.888]: +[006.889]: +[006.889]: +[006.890]: +[006.894]: +[006.894]: +[006.895]: +[006.896]: +[006.897]: +[006.953]: +[006.953]: +[006.955]: +[006.955]: +[006.956]: +[007.014]: +[007.014]: +[007.016]: +[007.016]: +[007.017]: +[007.548]: +[007.548]: +[007.550]: +[007.550]: +[007.551]: +[007.628]: +[007.628]: +[007.629]: +[007.630]: +[007.631]: +[008.119]: +[008.119]: +[008.120]: +[008.121]: +[008.122]: +[008.228]: +[008.229]: +[008.230]: +[008.230]: +[008.231]: +[008.739]: +[008.739]: +[008.740]: +[008.741]: +[008.742]: +[008.742]: send partition_complete_p4K_b256K.mbn, filesize=16384 +. +[008.748]: upgrade progress 0% 16384/191864239 +[008.749]: send finished +[008.750]: +[008.751]: +[008.752]: +[008.752]: +[008.753]: +[008.754]: send ../cefs.mbn, filesize=1826816 +....................... +[009.198]: upgrade progress 0% 1843200/191864239 +[009.198]: send finished +[009.200]: +[009.200]: +[009.201]: +[009.202]: +[009.203]: +[009.203]: send ../tz.mbn, filesize=913408 +............ +[009.430]: upgrade progress 1% 2756608/191864239 +[009.430]: send finished +[009.431]: +[009.431]: +[009.432]: +[009.433]: +[009.434]: +[009.434]: send ../devcfg.mbn, filesize=42379 +. +[009.455]: upgrade progress 1% 2798987/191864239 +[009.456]: send finished +[009.456]: +[009.457]: +[009.458]: +[009.459]: +[009.459]: +[009.460]: send ../apdp.mbn, filesize=13508 +. +[009.466]: upgrade progress 1% 2812495/191864239 +[009.466]: send finished +[009.468]: +[009.468]: +[009.469]: +[009.470]: +[009.471]: +[009.471]: send ../xbl_cfg.elf, filesize=65724 +. +[009.498]: upgrade progress 1% 2878219/191864239 +[009.498]: send finished +[009.499]: +[009.500]: +[009.501]: +[009.501]: +[009.502]: +[009.503]: send ../multi_image.mbn, filesize=13008 +. +[009.515]: upgrade progress 1% 2891227/191864239 +[009.516]: send finished +[009.517]: +[009.518]: +[009.519]: +[009.520]: +[009.520]: +[009.521]: send ../hyp.mbn, filesize=84416 +.. +[009.547]: upgrade progress 1% 2975643/191864239 +[009.547]: send finished +[009.548]: +[009.549]: +[009.550]: +[009.551]: +[009.551]: +[009.552]: send ../abl.elf, filesize=176128 +... +[009.599]: upgrade progress 1% 3151771/191864239 +[009.599]: send finished +[009.600]: +[009.601]: +[009.602]: +[009.603]: +[009.603]: +[009.604]: send ../uefi.elf, filesize=1191936 +............... +[009.897]: upgrade progress 2% 4343707/191864239 +[009.897]: send finished +[009.898]: +[009.898]: +[009.899]: +[009.900]: +[009.901]: +[009.901]: send ../tools.fv, filesize=393216 +..... +[009.995]: upgrade progress 2% 4736923/191864239 +[009.995]: send finished +[009.997]: +[009.997]: +[009.998]: +[009.999]: +[010.000]: +[010.000]: send ../sdxprairie-boot.img, filesize=10360832 +............................................................................................................................... +[012.503]: upgrade progress 7% 15097755/191864239 +[012.504]: send finished +[012.505]: +[012.506]: +[012.507]: +[012.508]: +[012.508]: +[012.509]: send ../sdxprairie-boot.img, filesize=10360832 +............................................................................................................................... +[014.934]: upgrade progress 13% 25458587/191864239 +[014.935]: send finished +[014.936]: +[014.937]: +[014.938]: +[014.939]: +[014.939]: +[014.940]: send ../NON-HLOS.ubi, filesize=76283904 +.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... +[034.852]: upgrade progress 53% 101742491/191864239 +[034.852]: send finished +[034.854]: +[034.855]: +[034.856]: +[034.857]: +[034.857]: +[034.858]: send ../sdxprairie-recoveryfs.ubi, filesize=14417920 +................................................................................................................................................................................ +[038.365]: upgrade progress 60% 116160411/191864239 +[038.365]: send finished +[038.367]: +[038.367]: +[038.368]: +[038.369]: +[038.370]: +[038.370]: send ../usrdata.ubi, filesize=3932160 +................................................ +[039.286]: upgrade progress 62% 120092571/191864239 +[039.287]: send finished +[039.288]: +[039.289]: +[039.290]: +[039.291]: +[039.291]: +[039.292]: send ../oemdata.ubi, filesize=3932160 +................................................ +[040.219]: upgrade progress 64% 124024731/191864239 +[040.220]: send finished +[040.221]: +[040.222]: +[040.223]: +[040.224]: +[040.224]: +[040.225]: send ../sdxprairie-sysfs.ubi, filesize=67108864 +.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... +[057.600]: upgrade progress 99% 191133595/191864239 +[057.600]: send finished +[057.602]: +[057.602]: +[057.603]: +[057.604]: +[057.605]: +[057.605]: send ../aop.mbn, filesize=154240 +.. +[057.647]: upgrade progress 100% 191864239/191864239 +[057.647]: send finished +[057.648]: +[057.649]: +[057.650]: +[057.651]: +[057.651]: +[057.652]: send ../sbl1.mbn, filesize=576404 +........ +[057.789]: upgrade progress 100% 192440643/191864239 +[057.789]: send finished +[057.790]: +[057.790]: +[057.791]: +[057.791]: +[057.792]: +[057.792]: +[057.800]: inf[0] ep_in -1/1024, errno = 71 (Protocol error) +[057.800]: qusb_noblock_read read=-1, errno: 71 (Protocol error) +[057.800]: qusb_noblock_read cur=0, min_size=1 +[057.800]: firehose_protocol.c fh_recv_cmd 327 fail +[057.800]: THE TOTAL DOWNLOAD TIME IS 53.750 s +[057.801]: Upgrade module successfully. +root@OpenWrt:~# \ No newline at end of file diff --git a/application/qfirehose/src/log/MCU_remote.log.txt b/application/qfirehose/src/log/MCU_remote.log.txt new file mode 100644 index 0000000..7a2ea80 --- /dev/null +++ b/application/qfirehose/src/log/MCU_remote.log.txt @@ -0,0 +1,38 @@ +root@OpenWrt:~# ./QFirehose -p 9008 +[000.000]: QFirehose Version: Quectel_LTE&5G_QFirehose_Linux&Android_V1.3.2 +[000.000]: Builded: May 18 2020 11:32:04 +[000.004]: [1] /sys/bus/usb/devices/2-1.4 2c7c/800/414 +[000.012]: P: /dev/bus/usb/002/005 idVendor=2c7c idProduct=0800 +[000.012]: C: /dev/bus/usb/002/005 bNumInterfaces: 5 +[000.014]: I: If#= 0 Alt= 0 #EPs= 2 Cls=ff Sub=ff Prot=30 +[000.015]: E: Ad=81 Atr=02 MxPS= 1024 Ivl=0ms +[000.015]: E: Ad=01 Atr=02 MxPS= 1024 Ivl=0ms +[000.016]: I: If#= 1 Alt= 0 #EPs= 3 Cls=ff Sub=00 Prot=00 +[000.017]: I: If#= 2 Alt= 0 #EPs= 3 Cls=ff Sub=00 Prot=00 +[000.018]: I: If#= 3 Alt= 0 #EPs= 3 Cls=ff Sub=00 Prot=00 +[000.019]: I: If#= 4 Alt= 0 #EPs= 3 Cls=ff Sub=ff Prot=ff +[000.021]: qusb_noblock_open port_name = /dev/ttyUSB0 +[000.024]: old software version: RM500QGLAAR01A01M4G_BETA_20200428F +[001.025]: poll_wait events=POLLIN msec=1000 timeout +[001.026]: qusb_noblock_read cur=0, min_size=1 +[001.026]: switch to 'Emergency download mode' +[001.031]: successful, wait module reboot +[002.034]: fail to fopen /sys/bus/usb/devices/2-1.4/uevent, errno: 2 (No such file or directory) +[003.038]: fail to fopen /sys/bus/usb/devices/2-1.4/uevent, errno: 2 (No such file or directory) +[004.041]: [1] /sys/bus/usb/devices/1-1.4 5c6/9008/0 +[004.046]: P: /dev/bus/usb/001/010 idVendor=05c6 idProduct=9008 +[004.046]: C: /dev/bus/usb/001/010 bNumInterfaces: 1 +[004.047]: I: If#= 0 Alt= 0 #EPs= 2 Cls=ff Sub=ff Prot=11 +[004.048]: E: Ad=81 Atr=02 MxPS= 512 Ivl=0ms +[004.049]: E: Ad=01 Atr=02 MxPS= 512 Ivl=0ms +[004.052]: create_tcp_server tcp_port=9008 +[004.053]: server_fd=4 +[004.055]: wait_client_connect +[023.889]: clientfd = 5 192.168.1.153:58069 connect +[023.889]: usb_fd = 6 +[083.310]: inf[0] ep_in -1/4096, errno = 71 (Protocol error) +[083.310]: qusb_noblock_read read=-1, errno: 71 (Protocol error) +[083.310]: qusb_noblock_read cur=0, min_size=1 +[083.310]: usb2tcp_main poll usb_fd = 6, revents = 0011 +root@OpenWrt:~# + diff --git a/application/qfirehose/src/log/Ubuntu_remote.log.txt b/application/qfirehose/src/log/Ubuntu_remote.log.txt new file mode 100644 index 0000000..f511e0f --- /dev/null +++ b/application/qfirehose/src/log/Ubuntu_remote.log.txt @@ -0,0 +1,607 @@ +carl@carl-Lenovo-ideapad-110-15ISK:~/Qfirehose_linux$ ./qfirehose -f ../EM20GRAR01A04V01M4G_factory -p 172.18.112.13:9008 +[000.000] QFirehose Version: LTE_QFirehose_Linux&Android_V1.1.8 +[000.000] Builded: Jul 5 2019 12:04:58 +[000.000] Find md5 check file <../EM20GRAR01A04V01M4G_factory/md5.txt> +[000.000] Totals checking 0 files md5 value, 0 file fail! +[000.001] qtcp_connect port_name = 172.18.112.13:9008 +[000.006] idVendor=2c7c, idProduct=0620, interfaceNum=6 +[001.008] poll_wait events=POLLIN msec=1000 timeout +[001.008] qusb_noblock_read cur=0, min_size=1 +[001.008] switch to 'Emergency download mode' +[001.011] successful, wait module reboot +[002.011] qtcp_connect port_name = 172.18.112.13:9008 +[002.014] idVendor=05c6, idProduct=9008, interfaceNum=1 +[002.014] dir=../EM20GRAR01A04V01M4G_factory/update/firehose +[002.014] dir=../EM20GRAR01A04V01M4G_factory/update/firehose +[002.014] d_name=prog_firehose_sdx24.mbn +[002.014] prog_nand_firehose_filename = prog_firehose_sdx24.mbn +[002.015] STATE <-- SAHARA_WAIT_HELLO +[002.015] RECEIVED <-- SAHARA_HELLO_ID +[002.015] RECEIVED <-- SAHARA_MODE_IMAGE_TX_PENDING +[002.015] SENDING --> SAHARA_HELLO_RESPONSE +[002.015] STATE <-- SAHARA_WAIT_COMMAND +[002.052] RECEIVED <-- SAHARA_READ_DATA_ID +[002.052] 0x0000000d 0x00000000 0x00000034 +[002.052] STATE <-- SAHARA_WAIT_COMMAND +[002.121] RECEIVED <-- SAHARA_READ_DATA_ID +[002.121] 0x0000000d 0x00000034 0x00000080 +[002.121] STATE <-- SAHARA_WAIT_COMMAND +[002.193] RECEIVED <-- SAHARA_READ_DATA_ID +[002.193] 0x0000000d 0x00001000 0x00001000 +[002.193] STATE <-- SAHARA_WAIT_COMMAND +[002.232] RECEIVED <-- SAHARA_READ_DATA_ID +[002.232] 0x0000000d 0x00002000 0x000009d0 +[002.233] STATE <-- SAHARA_WAIT_COMMAND +[002.272] RECEIVED <-- SAHARA_READ_DATA_ID +[002.272] 0x0000000d 0x00003000 0x00001000 +[002.273] STATE <-- SAHARA_WAIT_COMMAND +[002.312] RECEIVED <-- SAHARA_READ_DATA_ID +[002.312] 0x0000000d 0x00004000 0x00001000 +[002.313] STATE <-- SAHARA_WAIT_COMMAND +[002.353] RECEIVED <-- SAHARA_READ_DATA_ID +[002.353] 0x0000000d 0x00005000 0x00001000 +[002.353] STATE <-- SAHARA_WAIT_COMMAND +[002.392] RECEIVED <-- SAHARA_READ_DATA_ID +[002.392] 0x0000000d 0x00006000 0x00001000 +[002.392] STATE <-- SAHARA_WAIT_COMMAND +[002.432] RECEIVED <-- SAHARA_READ_DATA_ID +[002.432] 0x0000000d 0x00007000 0x00001000 +[002.433] STATE <-- SAHARA_WAIT_COMMAND +[002.473] RECEIVED <-- SAHARA_READ_DATA_ID +[002.473] 0x0000000d 0x00008000 0x00001000 +[002.473] STATE <-- SAHARA_WAIT_COMMAND +[002.513] RECEIVED <-- SAHARA_READ_DATA_ID +[002.513] 0x0000000d 0x00009000 0x00001000 +[002.513] STATE <-- SAHARA_WAIT_COMMAND +[002.552] RECEIVED <-- SAHARA_READ_DATA_ID +[002.552] 0x0000000d 0x0000a000 0x00001000 +[002.553] STATE <-- SAHARA_WAIT_COMMAND +[002.592] RECEIVED <-- SAHARA_READ_DATA_ID +[002.592] 0x0000000d 0x0000b000 0x00001000 +[002.593] STATE <-- SAHARA_WAIT_COMMAND +[002.633] RECEIVED <-- SAHARA_READ_DATA_ID +[002.633] 0x0000000d 0x0000c000 0x00001000 +[002.633] STATE <-- SAHARA_WAIT_COMMAND +[002.673] RECEIVED <-- SAHARA_READ_DATA_ID +[002.673] 0x0000000d 0x0000d000 0x00001000 +[002.673] STATE <-- SAHARA_WAIT_COMMAND +[002.712] RECEIVED <-- SAHARA_READ_DATA_ID +[002.713] 0x0000000d 0x0000e000 0x00001000 +[002.713] STATE <-- SAHARA_WAIT_COMMAND +[002.756] RECEIVED <-- SAHARA_READ_DATA_ID +[002.757] 0x0000000d 0x0000f000 0x00001000 +[002.757] STATE <-- SAHARA_WAIT_COMMAND +[002.796] RECEIVED <-- SAHARA_READ_DATA_ID +[002.796] 0x0000000d 0x00010000 0x00001000 +[002.796] STATE <-- SAHARA_WAIT_COMMAND +[002.837] RECEIVED <-- SAHARA_READ_DATA_ID +[002.837] 0x0000000d 0x00011000 0x00001000 +[002.837] STATE <-- SAHARA_WAIT_COMMAND +[002.876] RECEIVED <-- SAHARA_READ_DATA_ID +[002.876] 0x0000000d 0x00012000 0x00001000 +[002.877] STATE <-- SAHARA_WAIT_COMMAND +[002.916] RECEIVED <-- SAHARA_READ_DATA_ID +[002.917] 0x0000000d 0x00013000 0x00001000 +[002.917] STATE <-- SAHARA_WAIT_COMMAND +[002.957] RECEIVED <-- SAHARA_READ_DATA_ID +[002.957] 0x0000000d 0x00014000 0x00001000 +[002.957] STATE <-- SAHARA_WAIT_COMMAND +[002.997] RECEIVED <-- SAHARA_READ_DATA_ID +[002.997] 0x0000000d 0x00015000 0x00001000 +[002.997] STATE <-- SAHARA_WAIT_COMMAND +[003.036] RECEIVED <-- SAHARA_READ_DATA_ID +[003.036] 0x0000000d 0x00016000 0x00001000 +[003.037] STATE <-- SAHARA_WAIT_COMMAND +[003.076] RECEIVED <-- SAHARA_READ_DATA_ID +[003.076] 0x0000000d 0x00017000 0x00001000 +[003.076] STATE <-- SAHARA_WAIT_COMMAND +[003.117] RECEIVED <-- SAHARA_READ_DATA_ID +[003.117] 0x0000000d 0x00018000 0x00001000 +[003.117] STATE <-- SAHARA_WAIT_COMMAND +[003.157] RECEIVED <-- SAHARA_READ_DATA_ID +[003.157] 0x0000000d 0x00019000 0x00001000 +[003.157] STATE <-- SAHARA_WAIT_COMMAND +[003.196] RECEIVED <-- SAHARA_READ_DATA_ID +[003.197] 0x0000000d 0x0001a000 0x00001000 +[003.197] STATE <-- SAHARA_WAIT_COMMAND +[003.236] RECEIVED <-- SAHARA_READ_DATA_ID +[003.236] 0x0000000d 0x0001b000 0x00001000 +[003.237] STATE <-- SAHARA_WAIT_COMMAND +[003.276] RECEIVED <-- SAHARA_READ_DATA_ID +[003.276] 0x0000000d 0x0001c000 0x00001000 +[003.277] STATE <-- SAHARA_WAIT_COMMAND +[003.317] RECEIVED <-- SAHARA_READ_DATA_ID +[003.317] 0x0000000d 0x0001d000 0x00001000 +[003.317] STATE <-- SAHARA_WAIT_COMMAND +[003.356] RECEIVED <-- SAHARA_READ_DATA_ID +[003.356] 0x0000000d 0x0001e000 0x00001000 +[003.357] STATE <-- SAHARA_WAIT_COMMAND +[003.396] RECEIVED <-- SAHARA_READ_DATA_ID +[003.396] 0x0000000d 0x0001f000 0x00001000 +[003.397] STATE <-- SAHARA_WAIT_COMMAND +[003.437] RECEIVED <-- SAHARA_READ_DATA_ID +[003.437] 0x0000000d 0x00020000 0x00001000 +[003.437] STATE <-- SAHARA_WAIT_COMMAND +[003.476] RECEIVED <-- SAHARA_READ_DATA_ID +[003.477] 0x0000000d 0x00021000 0x00001000 +[003.477] STATE <-- SAHARA_WAIT_COMMAND +[003.516] RECEIVED <-- SAHARA_READ_DATA_ID +[003.517] 0x0000000d 0x00022000 0x00001000 +[003.517] STATE <-- SAHARA_WAIT_COMMAND +[003.556] RECEIVED <-- SAHARA_READ_DATA_ID +[003.556] 0x0000000d 0x00023000 0x00001000 +[003.557] STATE <-- SAHARA_WAIT_COMMAND +[003.597] RECEIVED <-- SAHARA_READ_DATA_ID +[003.597] 0x0000000d 0x00024000 0x00001000 +[003.597] STATE <-- SAHARA_WAIT_COMMAND +[003.637] RECEIVED <-- SAHARA_READ_DATA_ID +[003.637] 0x0000000d 0x00025000 0x00001000 +[003.637] STATE <-- SAHARA_WAIT_COMMAND +[003.676] RECEIVED <-- SAHARA_READ_DATA_ID +[003.676] 0x0000000d 0x00026000 0x00001000 +[003.677] STATE <-- SAHARA_WAIT_COMMAND +[003.716] RECEIVED <-- SAHARA_READ_DATA_ID +[003.716] 0x0000000d 0x00027000 0x00001000 +[003.717] STATE <-- SAHARA_WAIT_COMMAND +[003.757] RECEIVED <-- SAHARA_READ_DATA_ID +[003.757] 0x0000000d 0x00028000 0x00001000 +[003.757] STATE <-- SAHARA_WAIT_COMMAND +[003.797] RECEIVED <-- SAHARA_READ_DATA_ID +[003.797] 0x0000000d 0x00029000 0x00001000 +[003.797] STATE <-- SAHARA_WAIT_COMMAND +[003.836] RECEIVED <-- SAHARA_READ_DATA_ID +[003.837] 0x0000000d 0x0002a000 0x000005a8 +[003.837] STATE <-- SAHARA_WAIT_COMMAND +[003.876] RECEIVED <-- SAHARA_READ_DATA_ID +[003.876] 0x0000000d 0x0002a600 0x00001000 +[003.877] STATE <-- SAHARA_WAIT_COMMAND +[003.917] RECEIVED <-- SAHARA_READ_DATA_ID +[003.917] 0x0000000d 0x0002b600 0x00001000 +[003.917] STATE <-- SAHARA_WAIT_COMMAND +[003.957] RECEIVED <-- SAHARA_READ_DATA_ID +[003.957] 0x0000000d 0x0002c600 0x00001000 +[003.957] STATE <-- SAHARA_WAIT_COMMAND +[003.996] RECEIVED <-- SAHARA_READ_DATA_ID +[003.997] 0x0000000d 0x0002d600 0x00001000 +[003.997] STATE <-- SAHARA_WAIT_COMMAND +[004.036] RECEIVED <-- SAHARA_READ_DATA_ID +[004.036] 0x0000000d 0x0002e600 0x00001000 +[004.036] STATE <-- SAHARA_WAIT_COMMAND +[004.076] RECEIVED <-- SAHARA_READ_DATA_ID +[004.077] 0x0000000d 0x0002f600 0x00001000 +[004.077] STATE <-- SAHARA_WAIT_COMMAND +[004.116] RECEIVED <-- SAHARA_READ_DATA_ID +[004.117] 0x0000000d 0x00030600 0x00001000 +[004.117] STATE <-- SAHARA_WAIT_COMMAND +[004.156] RECEIVED <-- SAHARA_READ_DATA_ID +[004.156] 0x0000000d 0x00031600 0x00001000 +[004.157] STATE <-- SAHARA_WAIT_COMMAND +[004.196] RECEIVED <-- SAHARA_READ_DATA_ID +[004.196] 0x0000000d 0x00032600 0x00001000 +[004.197] STATE <-- SAHARA_WAIT_COMMAND +[004.237] RECEIVED <-- SAHARA_READ_DATA_ID +[004.237] 0x0000000d 0x00033600 0x00001000 +[004.237] STATE <-- SAHARA_WAIT_COMMAND +[004.277] RECEIVED <-- SAHARA_READ_DATA_ID +[004.277] 0x0000000d 0x00034600 0x00001000 +[004.277] STATE <-- SAHARA_WAIT_COMMAND +[004.316] RECEIVED <-- SAHARA_READ_DATA_ID +[004.316] 0x0000000d 0x00035600 0x00001000 +[004.317] STATE <-- SAHARA_WAIT_COMMAND +[004.356] RECEIVED <-- SAHARA_READ_DATA_ID +[004.357] 0x0000000d 0x00036600 0x00001000 +[004.357] STATE <-- SAHARA_WAIT_COMMAND +[004.397] RECEIVED <-- SAHARA_READ_DATA_ID +[004.397] 0x0000000d 0x00037600 0x00001000 +[004.397] STATE <-- SAHARA_WAIT_COMMAND +[004.437] RECEIVED <-- SAHARA_READ_DATA_ID +[004.437] 0x0000000d 0x00038600 0x00001000 +[004.437] STATE <-- SAHARA_WAIT_COMMAND +[004.476] RECEIVED <-- SAHARA_READ_DATA_ID +[004.477] 0x0000000d 0x00039600 0x00001000 +[004.477] STATE <-- SAHARA_WAIT_COMMAND +[004.516] RECEIVED <-- SAHARA_READ_DATA_ID +[004.516] 0x0000000d 0x0003a600 0x00001000 +[004.516] STATE <-- SAHARA_WAIT_COMMAND +[004.557] RECEIVED <-- SAHARA_READ_DATA_ID +[004.557] 0x0000000d 0x0003b600 0x00001000 +[004.557] STATE <-- SAHARA_WAIT_COMMAND +[004.597] RECEIVED <-- SAHARA_READ_DATA_ID +[004.597] 0x0000000d 0x0003c600 0x00001000 +[004.597] STATE <-- SAHARA_WAIT_COMMAND +[004.636] RECEIVED <-- SAHARA_READ_DATA_ID +[004.637] 0x0000000d 0x0003d600 0x00001000 +[004.637] STATE <-- SAHARA_WAIT_COMMAND +[004.676] RECEIVED <-- SAHARA_READ_DATA_ID +[004.676] 0x0000000d 0x0003e600 0x00001000 +[004.677] STATE <-- SAHARA_WAIT_COMMAND +[004.717] RECEIVED <-- SAHARA_READ_DATA_ID +[004.717] 0x0000000d 0x0003f600 0x00001000 +[004.717] STATE <-- SAHARA_WAIT_COMMAND +[004.756] RECEIVED <-- SAHARA_READ_DATA_ID +[004.757] 0x0000000d 0x00040600 0x00001000 +[004.757] STATE <-- SAHARA_WAIT_COMMAND +[004.796] RECEIVED <-- SAHARA_READ_DATA_ID +[004.797] 0x0000000d 0x00041600 0x00001000 +[004.797] STATE <-- SAHARA_WAIT_COMMAND +[004.836] RECEIVED <-- SAHARA_READ_DATA_ID +[004.836] 0x0000000d 0x00042600 0x00001000 +[004.837] STATE <-- SAHARA_WAIT_COMMAND +[004.880] RECEIVED <-- SAHARA_END_IMAGE_TX_ID +[004.880] image_id = 13, status = 0 +[004.880] SENDING --> SAHARA_DONE +[004.881] STATE <-- SAHARA_WAIT_DONE_RESP +[004.953] RECEIVED <-- SAHARA_DONE_RESP_ID +[004.953] image_tx_status = 1 +[004.953] Successfully uploaded all images +[004.953] Sahara protocol completed +[004.953] dir=../EM20GRAR01A04V01M4G_factory/update/firehose +[004.953] d_name=rawprogram_nand_p4K_b256K_factory.xml +[005.934] +[005.935] +[005.935] +[005.935] +[005.936] +[005.936] +[005.936] +[005.936] +[005.936] +[005.936] +[005.937] +[005.937] +[005.937] +[005.937] +[005.938] +[005.938] +[005.938] +[005.938] +[005.938] +[005.939] +[006.940] poll_wait events=POLLIN msec=1000 timeout +[006.940] qusb_noblock_read cur=0, min_size=1 +[006.940] firehose_protocol.c fh_recv_cmd 294 fail +[006.940] +[006.974] +[006.975] +[006.976] +[006.976] +[007.053] +[007.159] +[007.159] +[007.521] +[007.530] +[007.530] +[007.601] +[007.601] +[007.601] +[007.601] +[007.601] +[007.673] +[007.673] +[007.673] +[007.722] +[007.722] +[007.793] +[007.793] +[007.793] +[007.793] +[007.793] +[007.873] +[007.873] +[007.873] +[007.873] +[007.873] +[007.953] +[007.953] +[007.953] +[007.953] +[007.953] +[008.033] +[008.033] +[008.033] +[008.033] +[008.033] +[008.113] +[008.113] +[008.113] +[008.113] +[008.113] +[008.193] +[008.193] +[008.193] +[008.193] +[008.193] +[008.273] +[008.273] +[008.273] +[008.273] +[008.273] +[008.353] +[008.353] +[008.353] +[008.353] +[008.353] +[008.433] +[008.433] +[008.433] +[008.433] +[008.433] +[008.513] +[008.513] +[008.513] +[008.513] +[008.513] +[008.593] +[008.593] +[008.593] +[008.616] +[008.616] +[008.693] +[008.693] +[008.693] +[008.717] +[008.717] +[008.793] +[008.793] +[008.793] +[009.236] +[009.236] +[009.313] +[009.313] +[009.313] +[009.416] +[009.416] +[009.493] +[009.493] +[009.493] +[010.094] +[010.094] +[010.173] +[010.173] +[010.173] +[010.715] +[010.715] +[010.793] +[010.793] +[010.793] +[010.793] send partition_complete_p4K_b256K.mbn, filesize=16384 +[010.793] Upgrade progress: 0 +[010.793] send finished +[010.840] +[010.840] +[010.913] +[010.913] +[010.913] +[010.913] send ../cefs.mbn, filesize=1454080 +[011.487] send finished +[011.573] +[011.573] +[011.653] +[011.653] +[011.653] +[011.653] send ../tz.mbn, filesize=933888 +[011.697] Upgrade progress: 1 +[012.004] send finished +[012.093] +[012.093] +[012.173] +[012.173] +[012.173] +[012.173] send ../devcfg.mbn, filesize=40222 +[012.173] send finished +[012.233] +[012.233] +[012.313] +[012.313] +[012.313] +[012.313] send ../xbl_cfg.elf, filesize=53348 +[012.315] send finished +[012.377] +[012.377] +[012.453] +[012.453] +[012.453] +[012.453] send ../multi_image.mbn, filesize=13064 +[012.453] send finished +[012.500] +[012.501] +[012.573] +[012.573] +[012.573] +[012.573] send ../aop.mbn, filesize=142624 +[012.593] send finished +[012.676] +[012.676] +[012.753] +[012.753] +[012.753] +[012.753] send ../hyp.mbn, filesize=80192 +[012.759] send finished +[012.829] +[012.829] +[012.901] +[012.901] +[012.901] +[012.901] send ../abl.elf, filesize=151552 +[012.921] send finished +[013.005] +[013.005] +[013.081] +[013.081] +[013.081] +[013.081] send ../uefi.elf, filesize=1323008 +[013.233] Upgrade progress: 2 +[013.598] send finished +[013.685] +[013.685] +[013.761] +[013.761] +[013.761] +[013.761] send ../tools.fv, filesize=393216 +[013.874] send finished +[013.969] +[013.969] +[014.041] +[014.041] +[014.041] +[014.041] send ../sec.dat, filesize=80 +[014.041] send finished +[014.084] +[014.085] +[014.161] +[014.161] +[014.161] +[014.161] send ../sdxpoorwills-boot.img, filesize=9582592 +[014.303] Upgrade progress: 3 +[015.019] Upgrade progress: 4 +[015.731] Upgrade progress: 5 +[016.450] Upgrade progress: 6 +[017.173] Upgrade progress: 7 +[017.884] Upgrade progress: 8 +[018.196] send finished +[018.289] +[018.289] +[018.361] +[018.361] +[018.361] +[018.361] send ../sdxpoorwills-boot.img, filesize=9582592 +[018.722] Upgrade progress: 9 +[019.442] Upgrade progress: 10 +[020.154] Upgrade progress: 11 +[020.874] Upgrade progress: 12 +[021.594] Upgrade progress: 13 +[022.320] Upgrade progress: 14 +[022.397] send finished +[022.488] +[022.489] +[022.561] +[022.561] +[022.561] +[022.561] send ../NON-HLOS.ubi, filesize=60555264 +[023.140] Upgrade progress: 15 +[023.851] Upgrade progress: 16 +[024.566] Upgrade progress: 17 +[025.282] Upgrade progress: 18 +[026.022] Upgrade progress: 19 +[026.734] Upgrade progress: 20 +[027.460] Upgrade progress: 21 +[028.181] Upgrade progress: 22 +[028.903] Upgrade progress: 23 +[029.633] Upgrade progress: 24 +[030.360] Upgrade progress: 25 +[031.085] Upgrade progress: 26 +[031.812] Upgrade progress: 27 +[032.546] Upgrade progress: 28 +[033.278] Upgrade progress: 29 +[034.007] Upgrade progress: 30 +[034.738] Upgrade progress: 31 +[035.481] Upgrade progress: 32 +[036.214] Upgrade progress: 33 +[036.948] Upgrade progress: 34 +[037.683] Upgrade progress: 35 +[038.426] Upgrade progress: 36 +[039.167] Upgrade progress: 37 +[039.902] Upgrade progress: 38 +[040.649] Upgrade progress: 39 +[041.393] Upgrade progress: 40 +[042.135] Upgrade progress: 41 +[042.877] Upgrade progress: 42 +[043.629] Upgrade progress: 43 +[044.378] Upgrade progress: 44 +[045.121] Upgrade progress: 45 +[045.871] Upgrade progress: 46 +[046.627] Upgrade progress: 47 +[047.369] Upgrade progress: 48 +[048.127] Upgrade progress: 49 +[048.881] Upgrade progress: 50 +[048.917] send finished +[049.013] +[049.013] +[049.093] +[049.093] +[049.093] +[049.093] send ../sdxpoorwills-recoveryfs.ubi, filesize=17301504 +[049.711] Upgrade progress: 51 +[050.425] Upgrade progress: 52 +[051.143] Upgrade progress: 53 +[051.859] Upgrade progress: 54 +[052.577] Upgrade progress: 55 +[053.303] Upgrade progress: 56 +[054.029] Upgrade progress: 57 +[054.743] Upgrade progress: 58 +[055.472] Upgrade progress: 59 +[056.197] Upgrade progress: 60 +[056.421] send finished +[056.517] +[056.517] +[056.593] +[056.593] +[056.593] +[056.593] send ../usrdata.ubi, filesize=2097152 +[057.025] Upgrade progress: 61 +[057.422] send finished +[057.517] +[057.517] +[057.593] +[057.593] +[057.593] +[057.593] send ../sdxpoorwills-sysfs.ubi, filesize=64225280 +[057.854] Upgrade progress: 62 +[058.563] Upgrade progress: 63 +[059.274] Upgrade progress: 64 +[059.990] Upgrade progress: 65 +[060.714] Upgrade progress: 66 +[061.435] Upgrade progress: 67 +[062.150] Upgrade progress: 68 +[062.874] Upgrade progress: 69 +[063.602] Upgrade progress: 70 +[064.318] Upgrade progress: 71 +[065.048] Upgrade progress: 72 +[065.777] Upgrade progress: 73 +[066.498] Upgrade progress: 74 +[067.232] Upgrade progress: 75 +[067.964] Upgrade progress: 76 +[068.693] Upgrade progress: 77 +[069.423] Upgrade progress: 78 +[070.161] Upgrade progress: 79 +[070.898] Upgrade progress: 80 +[071.644] Upgrade progress: 81 +[072.378] Upgrade progress: 82 +[073.122] Upgrade progress: 83 +[073.865] Upgrade progress: 84 +[074.597] Upgrade progress: 85 +[075.343] Upgrade progress: 86 +[076.087] Upgrade progress: 87 +[076.828] Upgrade progress: 88 +[077.581] Upgrade progress: 89 +[078.333] Upgrade progress: 90 +[079.080] Upgrade progress: 91 +[079.823] Upgrade progress: 92 +[080.588] Upgrade progress: 93 +[081.340] Upgrade progress: 94 +[082.088] Upgrade progress: 95 +[082.838] Upgrade progress: 96 +[083.597] Upgrade progress: 97 +[084.353] Upgrade progress: 98 +[085.107] Upgrade progress: 99 +[085.613] send finished +[085.713] +[085.713] +[085.793] +[085.793] +[085.793] +[085.793] send ../sbl1.mbn, filesize=556180 +[085.965] Upgrade progress: 100 +[085.976] send finished +[086.069] +[086.069] +[086.106] +[086.107] +[086.107] +[086.107] +[086.107] qtcp_read read=0, errno: 0 (Success) +[086.107] qusb_noblock_read read=0, errno: 0 (Success) +[086.107] qusb_noblock_read cur=0, min_size=1 +[086.107] firehose_protocol.c fh_recv_cmd 294 fail +[086.107] THE TOTAL DOWNLOAD TIME IS 84.093 s +[086.107] Upgrade module successfully. diff --git a/application/qfirehose/src/log/pcie_mhi.log.txt b/application/qfirehose/src/log/pcie_mhi.log.txt new file mode 100644 index 0000000..dd6a02a --- /dev/null +++ b/application/qfirehose/src/log/pcie_mhi.log.txt @@ -0,0 +1,60 @@ +# ./QFirehose -p /dev/mhi_BHI -f v09 + +[000.000]: QFirehose Version: Quectel_LTE&5G_QFirehose_Linux&Android_V1.4.5 +[000.000]: Builded: Feb 10 2021 14:14:00 +[000.000]: Find md5 check file +[000.000]: Totals checking 0 files md5 value, 0 file fail! +[000.024]: switch_to_edl_mode +[001.027]: poll_wait events=POLLIN msec=1000 timeout +[001.027]: qusb_noblock_read cur=0, min_size=1 +[001.027]: switch to 'Emergency download mode' +[001.039]: successful, wait module reboot +[002.040]: bhi_ee = 6 +[004.105]: dir=v09/update/firehose +[004.105]: d_name=rawprogram_nand_p4K_b256K_factory.xml +[004.108]: +...... +[004.168]: +[005.170]: poll_wait events=POLLIN msec=1000 timeout +[005.170]: qusb_noblock_read cur=0, min_size=1 +[005.170]: firehose_protocol.c fh_recv_cmd 327 fail +[005.170]: +[005.178]: +[005.181]: +[005.186]: +[005.186]: +[005.193]: +[005.365]: +[005.368]: +[005.761]: +[005.777]: +...... +[011.965]: +[011.972]: +[011.976]: +[011.979]: +[011.979]: send partition_complete_p4K_b256K.mbn, filesize=16384 +. +[011.979]: upgrade progress 0% 16384/144903062 +[011.979]: send finished +[011.997]: +...... +[146.314]: +[146.321]: +[146.325]: +[146.328]: +[146.328]: send ../sbl1.mbn, filesize=566196 +. +[146.616]: upgrade progress 100% 145469258/144903062 +[146.616]: send finished +[146.834]: +[146.834]: +[146.840]: +[146.843]: +[146.847]: +[146.850]: +[147.852]: poll_wait events=POLLIN msec=1000 timeout +[147.852]: qusb_noblock_read cur=0, min_size=1 +[147.852]: firehose_protocol.c fh_recv_cmd 327 fail +[147.852]: THE TOTAL DOWNLOAD TIME IS 143.747 s +[147.852]: Upgrade module successfully. diff --git a/application/qfirehose/src/md5.c b/application/qfirehose/src/md5.c new file mode 100644 index 0000000..a04a1b3 --- /dev/null +++ b/application/qfirehose/src/md5.c @@ -0,0 +1,477 @@ +/* + Copyright 2023 Quectel Wireless Solutions Co.,Ltd + + Quectel hereby grants customers of Quectel a license to use, modify, + distribute and publish the Software in binary form provided that + customers shall have no right to reverse engineer, reverse assemble, + decompile or reduce to source code form any portion of the Software. + Under no circumstances may customers modify, demonstrate, use, deliver + or disclose any portion of the Software in source code form. +*/ + +#include "md5.h" +#include //for __BYTE_ORDER + +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a)&0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define SET(n) (*(uint32_t *)&ptr[(n)*4]) +#define GET(n) SET(n) +#else +#define SET(n) \ + (block[(n)] = (uint32_t)ptr[(n)*4] | ((uint32_t)ptr[(n)*4 + 1] << 8) | \ + ((uint32_t)ptr[(n)*4 + 2] << 16) | ((uint32_t)ptr[(n)*4 + 3] << 24)) +#define GET(n) (block[(n)]) +#endif + +extern char firehose_unzip_full_dir[256]; +extern char firehose_zip_name[80]; + +static const void *body(md5_ctx_t *ctx, const void *data, unsigned long size) +{ + const unsigned char *ptr; + uint32_t a, b, c, d; + uint32_t saved_a, saved_b, saved_c, saved_d; +#if __BYTE_ORDER != __LITTLE_ENDIAN + uint32_t block[16]; +#endif + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do + { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + + /* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + + /* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + + /* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + + /* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void md5_begin(md5_ctx_t *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void md5_hash(const void *data, size_t size, md5_ctx_t *ctx) +{ + uint32_t saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) + { + available = 64 - used; + + if (size < available) + { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) + { + data = body(ctx, data, size & ~((size_t)0x3f)); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +void md5_end(void *resbuf, md5_ctx_t *ctx) +{ + unsigned char *result = (unsigned char *)resbuf; + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) + { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + if (used >= 64) return (void)0; + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + memset(ctx, 0, sizeof(*ctx)); +} + +int md5sum(char *file, void *md5_buf) +{ + char buf[256]; + md5_ctx_t ctx; + int ret = 0; + FILE *f; + + f = fopen(file, "r"); + if (!f) return -1; + + md5_begin(&ctx); + do + { + int len = fread(buf, 1, sizeof(buf), f); + if (!len) break; + + md5_hash(buf, len, &ctx); + ret += len; + } while (1); + + md5_end(md5_buf, &ctx); + fclose(f); + + return ret; +} + +int md5_check(const char *firehose_dir) +{ + FILE *fp = NULL; + char md5_file_path[256], buff[256], file_name[128], file_name_tmp[128], file_full_path[256], + file_md5_value[64]; + char *ps = NULL, *pe = NULL; + unsigned char compute_md5_buff[16]; + char convert_md5_buff[33]; + int i, file_count = 0, fail_count = 0; + + if (is_upgrade_fimeware_zip_7z) + { + memset(zip_cmd_buf, 0, sizeof(zip_cmd_buf)); + + if (is_upgrade_fimeware_only_zip) + { + if (is_firehose_zip_7z_name_exit) + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), + "unzip -o -q %.240s %.76s/'*md5.txt' -d /tmp/ > %s", firehose_dir, + firehose_zip_name, ZIP_PROCESS_INFO); + } + else + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), + "unzip -o -q %.240s '*md5.txt' -d /tmp/ > %s", firehose_dir, + ZIP_PROCESS_INFO); + } + } + else + { + if (is_firehose_zip_7z_name_exit) + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), "7z x %.240s -o/tmp/ %.76s/md5.txt > %s", + firehose_dir, firehose_zip_name, ZIP_PROCESS_INFO); + } + else + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), "7z x %.240s -o/tmp/ md5.txt > %s", + firehose_dir, ZIP_PROCESS_INFO); + } + } + printf("%s zip_cmd_buf:%s\n", __func__, zip_cmd_buf); + + if (-1 == system(zip_cmd_buf)) + { + printf("%s system return error\n", __func__); + return -1; + } + usleep(1000); + snprintf(md5_file_path, sizeof(md5_file_path), "%.240s/md5.txt", firehose_unzip_full_dir); + if (access(md5_file_path, R_OK)) + { + dbg_time("Cann't find md5.txt in %s, Please check it!\n", md5_file_path); + return -1; + } + else + { + dbg_time("Find md5 check file <%s>\n", md5_file_path); + } + } + else + { + snprintf(md5_file_path, sizeof(md5_file_path), "%.240s/md5.txt", firehose_dir); + if (access(md5_file_path, R_OK)) + { + dbg_time("Cann't find md5.txt in %s, Please check it!\n", firehose_dir); + return 0; // allow skip md5 check by delete md5.txt + } + else + { + dbg_time("Find md5 check file <%s>\n", md5_file_path); + } + } + + fp = fopen(md5_file_path, "rb"); + if (fp == NULL) + { + dbg_time("fail to fopen(%s), error: %d (%s)\n", md5_file_path, errno, strerror(errno)); + return -1; + } + + while (fgets(buff, sizeof(buff), fp)) + { + if (strstr(buff, "targetfiles.zip")) continue; + + ps = strstr(buff, ":\\"); + if (ps == NULL) continue; + + file_count++; + ps = ps + 1; + pe = strstr(ps, ":"); + if (pe == NULL) continue; + + memcpy(file_name, ps, pe - ps); + file_name[pe - ps] = '\0'; + + for (i = 0; file_name[i] != '\0'; i++) + { + if (file_name[i] == '\\') file_name[i] = '/'; + } + + memcpy(file_md5_value, pe + 1, 32); + file_md5_value[32] = '\0'; + + if (is_upgrade_fimeware_zip_7z) + { + char *p1 = strchr(file_name, '/'); + memset(file_name_tmp, 0, sizeof(file_name_tmp)); + strncpy(file_name_tmp, p1 + 1, strlen(p1) - 1); + memset(zip_cmd_buf, 0, sizeof(zip_cmd_buf)); + + if (is_upgrade_fimeware_only_zip) + { + if (is_firehose_zip_7z_name_exit) + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), + "unzip -o -q %.240s %.76s/'*%.120s' -d /tmp/ > %s", firehose_dir, + firehose_zip_name, file_name_tmp, ZIP_PROCESS_INFO); + } + else + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), + "unzip -o -q %.240s '*%.120s' -d /tmp/ > %s", firehose_dir, + file_name_tmp, ZIP_PROCESS_INFO); + } + } + else + { + if (is_firehose_zip_7z_name_exit) + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), + "7z x %.240s -o/tmp/ %.76s/%.120s > %s", firehose_dir, + firehose_zip_name, file_name_tmp, ZIP_PROCESS_INFO); + } + else + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), "7z x %.240s -o/tmp/ %.120s > %s", + firehose_dir, file_name_tmp, ZIP_PROCESS_INFO); + } + } + dbg_time("%s zip_cmd_buf:%s\n", __func__, zip_cmd_buf); + + if (-1 == system(zip_cmd_buf)) + { + dbg_time("%s system return error\n", __func__); + return -1; + } + usleep(1000); + + snprintf(file_full_path, sizeof(file_full_path), "%.160s%.80s", firehose_unzip_full_dir, + file_name); + dbg_time("%s file_full_path:%s\n", __func__, file_full_path); + } + else + { + snprintf(file_full_path, sizeof(file_full_path), "%.160s%.80s", firehose_dir, + file_name); + } + + if (access(file_full_path, R_OK)) + { + continue; + } + + md5sum(file_full_path, compute_md5_buff); + + for (i = 0; i < 16; i++) + { + sprintf(convert_md5_buff + (i * 2), "%02X", compute_md5_buff[i]); + } + + if (strncasecmp(file_md5_value, convert_md5_buff, 16)) + { + fail_count++; + dbg_time("md5 checking: %s fail\n", file_full_path); + dbg_time("find %s, should be %s\n", file_md5_value, convert_md5_buff); + } + else + { + dbg_time("md5 checking: %s pass\n", file_full_path); + } + + if (is_upgrade_fimeware_zip_7z) + { + dbg_time("%s delet %s ...\n", __func__, file_full_path); + unlink(file_full_path); // delete all file + } + } + + fclose(fp); + if (is_upgrade_fimeware_zip_7z) + { + unlink(md5_file_path); // delete md5.txt + } + + dbg_time("Totals checking %d files md5 value, %d file fail!\n", file_count, fail_count); + + return (fail_count ? -1 : 0); +} diff --git a/application/qfirehose/src/md5.h b/application/qfirehose/src/md5.h new file mode 100644 index 0000000..20d6eb0 --- /dev/null +++ b/application/qfirehose/src/md5.h @@ -0,0 +1,33 @@ +/* + Copyright 2023 Quectel Wireless Solutions Co.,Ltd + + Quectel hereby grants customers of Quectel a license to use, modify, + distribute and publish the Software in binary form provided that + customers shall have no right to reverse engineer, reverse assemble, + decompile or reduce to source code form any portion of the Software. + Under no circumstances may customers modify, demonstrate, use, deliver + or disclose any portion of the Software in source code form. +*/ + +#ifndef _QUECTEL_MD5_H +#define _QUECTEL_MD5_H + +#include +#include +#include +#include +#include +#include +#include "usb_linux.h" + +typedef struct md5_ctx +{ + uint32_t lo, hi; + uint32_t a, b, c, d; + unsigned char buffer[64]; +} md5_ctx_t; + +// void dbg_time (const char *fmt, ...); +extern int md5_check(const char *); + +#endif /* _QUECTEL_MD5_H */ diff --git a/application/qfirehose/src/qfirehose.c b/application/qfirehose/src/qfirehose.c new file mode 100644 index 0000000..f86bf1e --- /dev/null +++ b/application/qfirehose/src/qfirehose.c @@ -0,0 +1,1115 @@ +/* + Copyright 2023 Quectel Wireless Solutions Co.,Ltd + + Quectel hereby grants customers of Quectel a license to use, modify, + distribute and publish the Software in binary form provided that + customers shall have no right to reverse engineer, reverse assemble, + decompile or reduce to source code form any portion of the Software. + Under no circumstances may customers modify, demonstrate, use, deliver + or disclose any portion of the Software in source code form. +*/ + +#include +#include +#include +#include +#ifdef USE_IPC_MSG +#include +#include +#endif + +#include "usb_linux.h" +#include "md5.h" + +/* +[PATCH 3.10 27/54] usb: xhci: Add support for URB_ZERO_PACKET to bulk/sg transfers +https://www.spinics.net/lists/kernel/msg2100618.html + +commit 4758dcd19a7d9ba9610b38fecb93f65f56f86346 +Author: Reyad Attiyat +Date: Thu Aug 6 19:23:58 2015 +0300 + + usb: xhci: Add support for URB_ZERO_PACKET to bulk/sg transfers + + This commit checks for the URB_ZERO_PACKET flag and creates an extra + zero-length td if the urb transfer length is a multiple of the endpoint's + max packet length. +*/ +unsigned qusb_zlp_mode = 1; // MT7621 donot support USB ZERO PACKET +unsigned q_erase_all_before_download = 0; +unsigned q_module_packet_sign = 0; +unsigned int g_from_ecm_to_rndis = 0; +const char *q_device_type = "nand"; // nand/emmc/ufs +int sahara_main(const char *firehose_dir, const char *firehose_mbn, void *usb_handle, int edl_mode_05c69008); +int firehose_main(const char *firehose_dir, void *usb_handle, unsigned qusb_zlp_mode); +int stream_download(const char *firehose_dir, void *usb_handle, unsigned qusb_zlp_mode); +int retrieve_soft_revision(void *usb_handle, uint8_t *mobile_software_revision, unsigned length); +int usb2tcp_main(const void *usb_handle, int tcp_port, unsigned qusb_zlp_mode); +int ql_capture_usbmon_log(const char *usbmon_logfile); +void ql_stop_usbmon_log(); + +// process vals +static long long all_bytes_to_transfer = 0; // need transfered +static long long transfer_bytes = 0; // transfered bytes; + +char zip_cmd_buf[512] = {0}; // zip cmd buf +char firehose_zip_name[80] = {0}; +char firehose_unzip_full_dir[256] = {0}; +file_name_backup file_name_b; +int is_upgrade_fimeware_zip_7z = 0; +int is_firehose_zip_7z_name_exit = 0; +int is_upgrade_fimeware_only_zip = 0; +int g_is_module_adb_entry_edl = 0; + +int g_is2mdn_path = 0; + +int switch_to_edl_mode(void *usb_handle) +{ + // DIAG commands used to switch the Qualcomm devices to EDL (Emergency download mode) + unsigned char edl_cmd[] = {0x4b, 0x65, 0x01, 0x00, 0x54, 0x0f, 0x7e}; + // unsigned char edl_cmd[] = {0x3a, 0xa1, 0x6e, 0x7e}; //DL (download mode) + unsigned char *pbuf = malloc(512); + if (pbuf == NULL) + { + return 0; + } + + int rx_len; + int rx_count = 0; + + do + { + rx_len = qusb_noblock_read(usb_handle, pbuf, 512, 0, 1000); + if (rx_count++ > 100) break; + } while (rx_len > 0); + + dbg_time("switch to 'Emergency download mode'\n"); + rx_len = qusb_noblock_write(usb_handle, edl_cmd, sizeof(edl_cmd), sizeof(edl_cmd), 3000, 0); + if (rx_len < 0) return 0; + + rx_count = 0; + + do + { + rx_len = qusb_noblock_read(usb_handle, pbuf, 512, 0, 3000); + if (rx_len == sizeof(edl_cmd) && memcmp(pbuf, edl_cmd, sizeof(edl_cmd)) == 0) + { + dbg_time("successful, wait module reboot\n"); + safe_free(pbuf); + return 1; + } + + if (rx_count++ > 50) break; + + } while (rx_len > 0); + + safe_free(pbuf); + return 0; +} + +int switch_to_edl_mode_in_adb_way() +{ + printf("entry switch_to_edl_mode_in_adb_way \r\n"); + int res = -1; + res = system("adb shell lxc-power1 adb host"); + // if (res == 127) + // { + // printf("call /bin/sh return error \r\n"); + // return res; + // } + // else if (res == -1) + // { + // printf("just return: error \r\n"); + // return res; + // } + // else if (res == 0) + // { + // printf("no child pid create: error \r\n"); + // return res; + // } + + printf("send lxc power1 success res=[%d] \r\n", res); + sleep(20); + res = system("adb reboot edl"); + // if (res == 127) + // { + // printf("call /bin/sh return error \r\n"); + // return res; + // } + // else if (res == -1) + // { + // printf("just return: error \r\n"); + // return res; + // } + // else if (res == 0) + // { + // printf("no child pid create: error \r\n"); + // return res; + // } + + printf("send reboot edl success res=[%d] \r\n", res); + return 0; +} + +static void usage(int status, const char *program_name) +{ + if (status != EXIT_SUCCESS) + { + printf("Try '%s --help' for more information.\n", program_name); + } + else + { + dbg_time("Upgrade Quectel's modules with Qualcomm's firehose protocol.\n"); + dbg_time("Usage: %s [options...]\n", program_name); + dbg_time(" -f [package_dir] Upgrade package directory path\n"); + dbg_time(" -p [/dev/ttyUSBx] Diagnose port, will auto-detect if not " + "specified\n"); + dbg_time(" -s [/sys/bus/usb/devices/xx] When multiple modules exist on the board, use " + "-s specify which module you want to upgrade\n"); + dbg_time(" -l [dir_name] Sync log into a file(will create " + "qfirehose_timestamp.log)\n"); + dbg_time(" -u [usbmon_log] Catch usbmon log and save to file (need " + "debugfs and usbmon driver)\n"); + dbg_time(" -n Skip MD5 check\n"); + dbg_time(" -d Device Type, default nand, support emmc/ufs\n"); + dbg_time(" -v For AG215S-GLR signed firmware packages\n"); + } + exit(status); +} + +/* +1. enum dir, fix up dirhose_dir +2. md5 examine +3. furture +*/ +static char *find_firehose_mbn(char **firehose_dir, size_t size) +{ + char *firehose_mbn = NULL; + + if (is_upgrade_fimeware_zip_7z) + { + int i; + char file_name_prog[128] = {0}; + char file_name_prog_dir[256] = {0}; + + firehose_mbn = (char *)malloc(256); + if (firehose_mbn == NULL) + { + return NULL; + } + + for (i = 0; i < file_name_b.file_name_count; i++) + { + if ((strstr(file_name_b.file_backup_c[i].zip_file_name_backup, "prog_nand_firehose_") && strstr(file_name_b.file_backup_c[i].zip_file_name_backup, ".mbn")) || + (strstr(file_name_b.file_backup_c[i].zip_file_name_backup, "prog_emmc_firehose_") && strstr(file_name_b.file_backup_c[i].zip_file_name_backup, ".mbn")) || + (strstr(file_name_b.file_backup_c[i].zip_file_name_backup, "prog_firehose_") && strstr(file_name_b.file_backup_c[i].zip_file_name_backup, ".mbn")) || + (strstr(file_name_b.file_backup_c[i].zip_file_name_backup, "prog_firehose_") && strstr(file_name_b.file_backup_c[i].zip_file_name_backup, ".elf")) || + (strstr(file_name_b.file_backup_c[i].zip_file_name_backup, "firehose-prog") && strstr(file_name_b.file_backup_c[i].zip_file_name_backup, ".mbn")) || + (strstr(file_name_b.file_backup_c[i].zip_file_name_backup, "prog_") && strstr(file_name_b.file_backup_c[i].zip_file_name_backup, ".mbn")) || + (strstr(file_name_b.file_backup_c[i].zip_file_name_backup, "xbl_s_devprg_Qcm8550_ns") && strstr(file_name_b.file_backup_c[i].zip_file_name_backup, ".melf")) || + (strstr(file_name_b.file_backup_c[i].zip_file_name_backup, "xbl_s_devprg_ns_SA52X") && strstr(file_name_b.file_backup_c[i].zip_file_name_backup, ".melf"))) + { + printf("file_name_b.file_backup_c[i].zip_file_name_backup:%s\n", file_name_b.file_backup_c[i].zip_file_name_backup); + printf("file_name_b.file_backup_c[i].zip_file_dir_backup:%s\n", file_name_b.file_backup_c[i].zip_file_dir_backup); + + if (strstr(file_name_b.file_backup_c[i].zip_file_dir_backup, "update/firehose")) + { + memmove(file_name_prog, file_name_b.file_backup_c[i].zip_file_name_backup, strlen(file_name_b.file_backup_c[i].zip_file_name_backup)); + memmove(file_name_prog_dir, file_name_b.file_backup_c[i].zip_file_dir_backup, strlen(file_name_b.file_backup_c[i].zip_file_dir_backup)); + break; + } + } + } + + if (file_name_prog[0] != '\0') + { + memset(zip_cmd_buf, 0, sizeof(zip_cmd_buf)); + if (is_upgrade_fimeware_only_zip) + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), "unzip -o -q %.240s '*%.200s' -d /tmp/ > %s", *firehose_dir, file_name_prog_dir, ZIP_PROCESS_INFO); + } + else + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), "7z x %.240s -o/tmp/ %.200s > %s", *firehose_dir, file_name_prog_dir, ZIP_PROCESS_INFO); + } + printf("%s zip_cmd_buf:%s\n", __func__, zip_cmd_buf); + if (-1 == system(zip_cmd_buf)) + { + printf("%s system return error\n", __func__); + return NULL; + } + usleep(1000); + + memmove(firehose_mbn, file_name_prog_dir, 240); + } + } + else + { + if (strstr(*firehose_dir, "/update/firehose") == NULL) + { + size_t len = strlen(*firehose_dir); + + strncat(*firehose_dir, "/update/firehose", size); + if (access(*firehose_dir, R_OK)) + { + (*firehose_dir)[len] = '\0'; // for smart module + } + } + + if (access(*firehose_dir, R_OK)) + { + dbg_time("%s access(%s fail), errno: %d (%s)\n", __func__, *firehose_dir, errno, strerror(errno)); + return NULL; + } + + if (!qfile_find_file(*firehose_dir, "prog_nand_firehose_", ".mbn", &firehose_mbn) && !qfile_find_file(*firehose_dir, "prog_emmc_firehose_", ".mbn", &firehose_mbn) && + !qfile_find_file(*firehose_dir, "prog_firehose_", ".mbn", &firehose_mbn) && !qfile_find_file(*firehose_dir, "prog_firehose_", ".elf", &firehose_mbn) && + !qfile_find_file(*firehose_dir, "firehose-prog", ".mbn", &firehose_mbn) && !qfile_find_file(*firehose_dir, "prog_", ".mbn", &firehose_mbn) && + !qfile_find_file(*firehose_dir, "xbl_s_devprg_Qcm8550_ns", ".melf", + &firehose_mbn) // smart SA885GAPNA + && !qfile_find_file(*firehose_dir, "xbl_s_devprg_ns_SA52X", ".melf", + &firehose_mbn) // AG590ECNABR01A01M8G_OCPU_01.001.01 + ) + { + dbg_time("%s fail to find firehose mbn file in %s\n", __func__, *firehose_dir); + safe_free(firehose_mbn); + return NULL; + } + } + + dbg_time("%s %s\n", __func__, firehose_mbn); + return firehose_mbn; +} + +#if 0 +static int detect_and_judge_module_version(void *usb_handle) { + static uint8_t version[64] = {'\0'}; + + if (usb_handle && version[0] == '\0') { + retrieve_soft_revision(usb_handle, version, sizeof(version)); + if (version[0]) { + size_t i = 0; + size_t length = strlen((const char *)version) - strlen("R00A00"); + dbg_time("old software version: %s\n", version); + for (i = 0; i < length; i++) { + if (version[i] == 'R' && isdigit(version[i+1]) && isdigit(version[i+2]) + && version[i+3] == 'A' && isdigit(version[i+4]) && isdigit(version[i+5])) + { + version[i] = '\0'; + //dbg_time("old hardware version: %s\n", mobile_software_revision); + break; + } + } + } + } + + if (version[0]) + return 0; + + error_return(); +} +#endif + +FILE *loghandler = NULL; +#ifdef FIREHOSE_ENABLE +int firehose_main_entry(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + int opt; + int check_hash = 1; + int retval; + void *usb_handle = NULL; + int idVendor = 0, idProduct = 0, interfaceNum = 0; + int edl_retry = 30; // SDX55 require long time by now 20190412 + double start; + + // char *firehose_mbn = NULL; + int usb3_speed; + struct timespec usb3_atime; + int usb2tcp_port = 0; + char filename[128] = {'\0'}; + char *usbmon_logfile = NULL; + + char *file_message = malloc(MAX_PATH); + if (file_message == NULL) + { + return -1; + } + + char *firehose_mbn = malloc(MAX_PATH); + if (firehose_mbn == NULL) + { + safe_free(file_message); + return -1; + } + + char *firehose_dir = malloc(MAX_PATH); + if (firehose_dir == NULL) + { + safe_free(file_message); + safe_free(firehose_mbn); + return -1; + } + + char *module_port_name = malloc(MAX_PATH); + if (module_port_name == NULL) + { + safe_free(file_message); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + return -1; + } + + char *module_sys_path = malloc(MAX_PATH); + if (module_sys_path == NULL) + { + safe_free(file_message); + safe_free(module_port_name); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + return -1; + } + + memset(firehose_dir, 0, MAX_PATH); + memset(module_port_name, 0, MAX_PATH); + memset(module_sys_path, 0, MAX_PATH); + + // firehose_dir[0] = module_port_name[0] = module_sys_path[0] = '\0'; + + /* set file priviledge mask 0 */ + umask(0); + /*build V1.0.8*/ + dbg_time("Version: QFirehose_Linux_Android_V1.4.21\n"); // when release, + // rename to V1.X +#ifndef __clang__ + dbg_time("Builded: %s %s\n", __DATE__, __TIME__); +#endif + +#ifdef ANDROID + struct passwd *pd; + pd = getpwuid(getuid()); + dbg_time("------------------\n"); + dbg_time("User:\t %s\n", pd->pw_name); + struct group *group; + group = getgrgid(pd->pw_gid); + dbg_time("Group:\t %s\n", group->gr_name); + dbg_time("------------------\n"); +#if 0 // not all customers need this function + loghandler = fopen("/data/upgrade.log", "w+"); +#endif + if (loghandler) dbg_time("upgrade log will be sync to /data/upgrade.log\n"); +#endif + + optind = 1; + while (-1 != (opt = getopt(argc, argv, "f:p:z:s:l:u:d:nevhr"))) + { + switch (opt) + { + case 'n': check_hash = 0; break; + case 'l': + if (loghandler) fclose(loghandler); + snprintf(filename, sizeof(filename), "%.80s/qfirehose_%lu.log", optarg, time(NULL)); + loghandler = fopen(filename, "w+"); + if (loghandler) dbg_time("upgrade log will be sync to %s\n", filename); + break; + case 'f': { + strncpy(file_message, optarg, MAX_PATH - 1); + if (strstr(file_message, ".mbn") != NULL || strstr(file_message, ".elf") != NULL) + { + g_is2mdn_path = 1; + char *tmp = strrchr(file_message, '/'); + strncpy(firehose_mbn, tmp + 1, strlen(tmp) - 1); + strncpy(firehose_dir, file_message, strlen(file_message) - strlen(tmp)); + dbg_time("f pargram: " + "g_is2mdn_path=[%d],file_message=[%s],firehose_mbn=[%s]," + "firehose_dir=[%s]\n", + g_is2mdn_path, file_message, firehose_mbn, firehose_dir); + break; + } + strncpy(firehose_dir, file_message, strlen(file_message)); + break; + } + case 'p': + strncpy(module_port_name, optarg, MAX_PATH - 1); + if (!strcmp(module_port_name, "9008")) + { + usb2tcp_port = atoi(module_port_name); + module_port_name[0] = '\0'; + } + break; + case 's': + strncpy(module_sys_path, optarg, MAX_PATH - 1); + int len = strlen(optarg); + if (len > 256) + { + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + printf("optarg length is longer than 256\n"); + return -1; + } + + if (module_sys_path[strlen(optarg) - 1] == '/') module_sys_path[strlen(optarg) - 1] = '\0'; + break; + case 'z': qusb_zlp_mode = !!atoi(optarg); break; + case 'e': q_erase_all_before_download = 1; break; + case 'u': + usbmon_logfile = strdup(optarg); + if (usbmon_logfile == NULL) + { + printf("usbmon_logfile is NULL\n"); + return -1; + } + break; + case 'd': + q_device_type = strdup(optarg); + if (q_device_type == NULL) + { + printf("q_device_type is NULL\n"); + return -1; + } + break; + case 'v': q_module_packet_sign = 1; break; + case 'r': + g_from_ecm_to_rndis = 1; + printf("will use rndis mode [%d]\r\n", g_from_ecm_to_rndis); + break; + case 'h': usage(EXIT_SUCCESS, argv[0]); break; + default: break; + } + } + + if (usbmon_logfile) ql_capture_usbmon_log(usbmon_logfile); + + update_transfer_bytes(0); + if (usb2tcp_port) goto _usb2tcp_start; + + if (firehose_dir[0] == '\0') + { + usage(EXIT_SUCCESS, argv[0]); + update_transfer_bytes(-1); + error_return(); + } + + if (access(firehose_dir, R_OK)) + { + dbg_time("fail to access %s, errno: %d (%s)\n", firehose_dir, errno, strerror(errno)); + update_transfer_bytes(-1); + safe_free(firehose_dir); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(file_message); + safe_free(firehose_mbn); + error_return(); + } + + opt = strlen(firehose_dir); + if (firehose_dir[opt - 1] == '/') + { + firehose_dir[opt - 1] = '\0'; + } + + char buff[256] = {0}; + int file_name_count = 0; + + if (strstr(firehose_dir, ".zip") || strstr(firehose_dir, ".7z")) + { + if (strstr(firehose_dir, ".zip")) + { + is_upgrade_fimeware_only_zip = 1; + } + + unlink(ZIP_INFO); + memset(zip_cmd_buf, 0, sizeof(zip_cmd_buf)); + if (is_upgrade_fimeware_only_zip) + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), "unzip -l -q %.240s > %s", firehose_dir, ZIP_INFO); + } + else + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), "7z l %.240s > %s", firehose_dir, ZIP_INFO); + } + if (-1 == system(zip_cmd_buf)) + { + dbg_time("%s system return error\n", __func__); + return -1; + } + usleep(1000); + + char *p = strrchr(firehose_dir, '/'); // firehose_dir is the absolute path of the zip/7z + // file + if (p) + { + if (strstr(firehose_dir, ".zip")) + { + strncpy(firehose_zip_name, p + 1, strlen(p) - 4 - 1); // 4(.zip); 1(/) + } + else + { + strncpy(firehose_zip_name, p + 1, strlen(p) - 3 - 1); // 3(.7z); 1(/) + } + } + else + { + if (strstr(firehose_dir, ".zip")) + { + strncpy(firehose_zip_name, firehose_dir, + strlen(firehose_dir) - 4); // QFirehose -f RG520NEUDCR01A01M4G_01.001.01.001.zip + } + else + { + strncpy(firehose_zip_name, firehose_dir, + strlen(firehose_dir) - 3); // QFirehose -f RG520NEUDCR01A01M4G_01.001.01.001.7z + } + } + + dbg_time("firehose_zip_name:%s\n", firehose_zip_name); // RG520NEUDCR01A01M4G_01.001.01.001 + is_upgrade_fimeware_zip_7z = 1; // Judging as a zip/7z package upgrade + + if (!access(ZIP_INFO, F_OK)) + { + char *p0 = NULL; + char *p01 = NULL; + char *p1 = NULL; + char *p2 = NULL; + char *p3 = NULL; + char *p4 = NULL; + FILE *fp = fopen(ZIP_INFO, "rb"); + if (fp == NULL) + { + dbg_time("fail to fopen(%s), error: %d (%s)\n", ZIP_INFO, errno, strerror(errno)); + return -1; + } + + while (fgets(buff, sizeof(buff), fp)) + { + p0 = strstr(buff, firehose_zip_name); + if (p0) + { + int length_debug1 = strlen(p0); + if (p0[length_debug1 - 1] == 0x0a) length_debug1 -= 1; + + memmove(file_name_b.file_backup_c[file_name_count].zip_file_dir_backup, p0, length_debug1); + + p01 = strrchr(p0, '/'); + if (p01 == NULL) continue; + + if (p01[0] == '/' && p01[1] == '\0') + { + continue; + } + + is_firehose_zip_7z_name_exit = 1; // Determine which type of package it is and whether it should be placed + // in one folder or several files or folders after decompression + + int length_debug = strlen(p01); + if (p01[length_debug - 1] == 0x0a) length_debug -= 1; + + memmove(file_name_b.file_backup_c[file_name_count].zip_file_name_backup, p01 + 1, length_debug - 1); + + file_name_count++; + file_name_b.file_name_count = file_name_count; + } + else + { + p1 = strstr(buff, "contents.xml"); + p2 = strstr(buff, "md5.txt"); + p3 = strstr(buff, "update"); + + if (p1) + { + int length_debug1 = strlen(p1); + if (p1[length_debug1 - 1] == 0x0a) length_debug1 -= 1; + + memmove(file_name_b.file_backup_c[file_name_count].zip_file_dir_backup, p1, length_debug1); + + int length_debug = strlen(p1); + if (p1[length_debug - 1] == 0x0a) length_debug -= 1; + + memmove(file_name_b.file_backup_c[file_name_count].zip_file_name_backup, p1 + 1, length_debug - 1); + + file_name_count++; + file_name_b.file_name_count = file_name_count; + } + else if (p2) + { + int length_debug1 = strlen(p2); + if (p2[length_debug1 - 1] == 0x0a) length_debug1 -= 1; + + memmove(file_name_b.file_backup_c[file_name_count].zip_file_dir_backup, p2, length_debug1); + + int length_debug = strlen(p2); + if (p2[length_debug - 1] == 0x0a) length_debug -= 1; + + memmove(file_name_b.file_backup_c[file_name_count].zip_file_name_backup, p2 + 1, length_debug - 1); + + file_name_count++; + file_name_b.file_name_count = file_name_count; + } + else if (p3) + { + int length_debug1 = strlen(p3); + if (p3[length_debug1 - 1] == 0x0a) length_debug1 -= 1; + + memmove(file_name_b.file_backup_c[file_name_count].zip_file_dir_backup, p3, length_debug1); + + p4 = strrchr(p3, '/'); + if (p4 == NULL) continue; + + if (p4[0] == '/' && p4[1] == '\0') + { + dbg_time("continue..\n"); + continue; + } + + int length_debug = strlen(p4); + if (p4[length_debug - 1] == 0x0a) length_debug -= 1; + + memmove(file_name_b.file_backup_c[file_name_count].zip_file_name_backup, p4 + 1, length_debug - 1); + + file_name_count++; + file_name_b.file_name_count = file_name_count; + } + } + } + + fclose(fp); + unlink(ZIP_INFO); + + if (!is_firehose_zip_7z_name_exit) + { + memset(firehose_zip_name, 0, sizeof(firehose_zip_name)); + } + + if (firehose_zip_name[0] == '\0') + { + strcpy(firehose_unzip_full_dir, "/tmp"); + } + else + { + snprintf(firehose_unzip_full_dir, sizeof(firehose_unzip_full_dir), "/tmp/%.76s", firehose_zip_name); + } + + dbg_time("%s firehose_unzip_full_dir:%s\n", __func__, firehose_unzip_full_dir); + } + } + + if (check_hash && md5_check(firehose_dir)) + { + update_transfer_bytes(-1); + safe_free(firehose_dir); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(file_message); + safe_free(firehose_mbn); + error_return(); + } + + if (!g_is2mdn_path) firehose_mbn = find_firehose_mbn(&firehose_dir, MAX_PATH); + dbg_time("%s %s\n", __func__, firehose_mbn); + if (!firehose_mbn) + { + update_transfer_bytes(-1); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + error_return(); + } + + if (module_port_name[0] && !strncmp(module_port_name, "/dev/mhi", strlen("/dev/mhi"))) + { + if (qpcie_open(firehose_dir, firehose_mbn, module_port_name)) + { + update_transfer_bytes(-1); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + error_return(); + } + + usb_handle = &edl_pcie_mhifd; + start = get_now(); + goto __firehose_main; + } + else if (module_port_name[0] && strstr(module_port_name, ":9008")) + { + strcpy(module_sys_path, module_port_name); + goto __edl_retry; + } + +_usb2tcp_start: + if (module_sys_path[0] && access(module_sys_path, R_OK)) + { + dbg_time("fail to access %s, errno: %d (%s)\n", module_sys_path, errno, strerror(errno)); + update_transfer_bytes(-1); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + error_return(); + } + + if (module_port_name[0] && access(module_port_name, R_OK | W_OK)) + { + dbg_time("fail to access %s, errno: %d (%s)\n", module_port_name, errno, strerror(errno)); + update_transfer_bytes(-1); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + error_return(); + } + + if (module_sys_path[0] == '\0' && module_port_name[0] != '\0') + { + // get sys path by port name + quectel_get_syspath_name_by_ttyport(module_port_name, module_sys_path, MAX_PATH); + } + + g_is_module_adb_entry_edl = 0; + + if (module_sys_path[0] == '\0') + { + int module_count = auto_find_quectel_modules(module_sys_path, MAX_PATH, NULL, NULL); + if (module_count <= 0) + { + dbg_time("Quectel module not found\n"); + update_transfer_bytes(-1); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + error_return(); + } + else if (module_count == 1) + { + if (g_is_module_adb_entry_edl > 0) + { + switch_to_edl_mode_in_adb_way(); + } + } + else + { + dbg_time("There are multiple quectel modules in system, Please use <-s " + "/sys/bus/usb/devices/xx> specify which module you want to " + "upgrade!\n"); + dbg_time("The module's path was printed in the " + "previous log!\n"); + update_transfer_bytes(-1); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + error_return(); + } + } + +__edl_retry: + qusb_read_speed_atime(module_sys_path, &usb3_atime, &usb3_speed); + while (edl_retry-- > 0) + { + usb_handle = qusb_noblock_open(module_sys_path, &idVendor, &idProduct, &interfaceNum); + + if (usb_handle) + { + clock_gettime(CLOCK_REALTIME, &usb3_atime); + } + else + { + sleep(1); // in reset sate, wait connect + if (usb3_speed >= 5000 && access(module_sys_path, R_OK) && errno_nodev()) + { + if (auto_find_quectel_modules(module_sys_path, MAX_PATH, "5c6/9008/", &usb3_atime) > 1) + { + dbg_time("There are multiple quectel EDL modules in system!\n"); + update_transfer_bytes(-1); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + error_return(); + } + } + continue; + } + +#if 0 + if (idVendor == 0x2c7c && interfaceNum > 1) { + if (detect_and_judge_module_version(usb_handle)) { + // update_transfer_bytes(-1); + /* do not return here, this command will fail when modem is not ready */ + // error_return(); + } + } +#endif + + if (interfaceNum == 1) + { + if ((idVendor == 0x2C7C) && (idProduct == 0x0800)) + { + // although 5G module stay in dump mode, after send edl command, it also + // can enter edl mode + dbg_time("5G module stay in dump mode!\n"); + } + else + { + break; + } + dbg_time("something went wrong???, why only one interface left\n"); + } + + switch_to_edl_mode(usb_handle); + qusb_noblock_close(usb_handle); + usb_handle = NULL; + sleep(1); // wait usb disconnect and re-connect + } + + if (usb_handle == NULL) + { + update_transfer_bytes(-1); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + error_return(); + } + + if (usb2tcp_port) + { + retval = usb2tcp_main(usb_handle, usb2tcp_port, qusb_zlp_mode); + qusb_noblock_close(usb_handle); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(usbmon_logfile); + safe_free(firehose_dir); + safe_free(file_message); + safe_free(firehose_mbn); + return retval; + } + + start = get_now(); + retval = sahara_main(firehose_dir, firehose_mbn, usb_handle, idVendor == 0x05c6); + + if (!retval) + { + if (idVendor != 0x05C6) + { + sleep(1); + stream_download(firehose_dir, usb_handle, qusb_zlp_mode); + qusb_noblock_close(usb_handle); + sleep(10); // EM05-G switching to download mode is slow and increases the waiting time + // to 10 seconds + goto __edl_retry; + } + + __firehose_main: + retval = firehose_main(firehose_dir, usb_handle, qusb_zlp_mode); + if (retval == 0) + { + get_duration(start); + } + } + + qusb_noblock_close(usb_handle); + + safe_free(firehose_dir); + safe_free(module_port_name); + safe_free(module_sys_path); + safe_free(file_message); + safe_free(firehose_mbn); + + dbg_time("Upgrade module %s.\n", retval == 0 ? "successfully" : "failed"); + if (loghandler) fclose(loghandler); + if (retval) update_transfer_bytes(-1); + if (usbmon_logfile) ql_stop_usbmon_log(); + unlink(ZIP_PROCESS_INFO); + + return retval; +} + +double get_now() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; +} + +void get_duration(double start) { dbg_time("THE TOTAL DOWNLOAD TIME IS %.3f s\n", (get_now() - start)); } + +void set_transfer_allbytes(long long bytes) +{ + transfer_bytes = 0; + all_bytes_to_transfer = bytes; +} + +int update_progress_msg(int percent); +int update_progress_file(int percent); +/* +return percent +*/ +int update_transfer_bytes(long long bytes_cur) +{ + static int last_percent = -1; + int percent = 0; + + if (bytes_cur == -1 || bytes_cur == 0) + { + percent = bytes_cur; + } + else + { + transfer_bytes += bytes_cur; + percent = (transfer_bytes * 100) / all_bytes_to_transfer; + } + + if (percent != last_percent) + { + last_percent = percent; +#ifdef USE_IPC_FILE + update_progress_file(percent); +#endif +#ifdef USE_IPC_MSG + update_progress_msg(percent); +#endif + } + + return percent; +} + +void show_progress() +{ + static int percent = 0; + + if (all_bytes_to_transfer) percent = (transfer_bytes * 100) / all_bytes_to_transfer; + dbg_time("upgrade progress %d%% %lld/%lld\n", percent, transfer_bytes, all_bytes_to_transfer); +} + +#ifdef USE_IPC_FILE +#define IPC_FILE_ANDROID "/data/update.conf" +#define IPC_FILE_LINUX "/tmp/update.conf" +int update_progress_file(int percent) +{ + static int ipcfd = -1; + char buff[16]; + + if (ipcfd < 0) + { +#ifdef ANDROID + const char *ipc_file = IPC_FILE_ANDROID; +#else + const char *ipc_file = IPC_FILE_LINUX; +#endif + /* Have set umask previous, no need to call fchmod */ + ipcfd = open(ipc_file, O_TRUNC | O_CREAT | O_WRONLY | O_NONBLOCK, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (ipcfd < 0) + { + dbg_time("Fail to open(O_WRONLY) %s: %s\n", ipc_file, strerror(errno)); + return -1; + } + } + + lseek(ipcfd, 0, SEEK_SET); + snprintf(buff, sizeof(buff), "%d", percent); + if (write(ipcfd, buff, strlen(buff)) < 0) dbg_time("fail to write upgrade progress into %s: %s\n", ipc_file, strerror(errno)); + + if (percent == 100 || percent < 0) close(ipcfd); + return 0; +} +#endif + +#ifdef USE_IPC_MSG +#define MSGBUFFSZ 16 +struct message +{ + long mtype; + char mtext[MSGBUFFSZ]; +}; + +#define MSG_FILE "/etc/passwd" +#define MSG_TYPE_IPC 1 +static int msg_get() +{ + key_t key = ftok(MSG_FILE, 'a'); + int msgid = msgget(key, IPC_CREAT | 0644); + + if (msgid < 0) + { + dbg_time("msgget fail: key %d, %s\n", key, strerror(errno)); + return -1; + } + return msgid; +} + +static int msg_rm(int msgid) { return msgctl(msgid, IPC_RMID, 0); } + +static int msg_send(int msgid, long type, const char *msg) +{ + struct message info; + info.mtype = type; + snprintf(info.mtext, MSGBUFFSZ, "%s", msg); + if (msgsnd(msgid, (void *)&info, MSGBUFFSZ, IPC_NOWAIT) < 0) + { + dbg_time("msgsnd faild: msg %s, %s\n", msg, strerror(errno)); + return -1; + } + return 0; +} + +static int msg_recv(int msgid, struct message *info) +{ + if (msgrcv(msgid, (void *)info, MSGBUFFSZ, info->mtype, IPC_NOWAIT) < 0) + { + dbg_time("msgrcv faild: type %ld, %s\n", info->mtype, strerror(errno)); + return -1; + } + return 0; +} + +/** + * this function will not delete the msg queue + */ +int update_progress_msg(int percent) +{ + char buff[MSGBUFFSZ]; + int msgid = msg_get(); + if (msgid < 0) return -1; + snprintf(buff, sizeof(buff), "%d", percent); + +#ifndef IPC_TEST + return msg_send(msgid, MSG_TYPE_IPC, buff); +#else + msg_send(msgid, MSG_TYPE_IPC, buff); + struct message info; + info.mtype = MSG_TYPE_IPC; + msg_recv(msgid, &info); + printf("msg queue read: %s\n", info.mtext); +#endif +} +#endif diff --git a/application/qfirehose/src/sahara.c b/application/qfirehose/src/sahara.c new file mode 100644 index 0000000..918b99e --- /dev/null +++ b/application/qfirehose/src/sahara.c @@ -0,0 +1,529 @@ +/* + Copyright 2023 Quectel Wireless Solutions Co.,Ltd + + Quectel hereby grants customers of Quectel a license to use, modify, + distribute and publish the Software in binary form provided that + customers shall have no right to reverse engineer, reverse assemble, + decompile or reduce to source code form any portion of the Software. + Under no circumstances may customers modify, demonstrate, use, deliver + or disclose any portion of the Software in source code form. +*/ + +#include "usb_linux.h" +#include "sahara.h" + +static uint32_t le_uint32(uint32_t v32) +{ + const int is_bigendian = 1; + uint32_t tmp = v32; + if ((*(char *)&is_bigendian) == 0) + { + unsigned char *s = (unsigned char *)(&v32); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; + } + return tmp; +} + +static uint64_t le_uint64(uint64_t v64) +{ + const int is_bigendian = 1; + uint64_t tmp = v64; + if ((*(char *)&is_bigendian) == 0) + { + unsigned char *s = (unsigned char *)(&v64); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[7]; + d[1] = s[6]; + d[2] = s[5]; + d[3] = s[4]; + d[4] = s[3]; + d[5] = s[2]; + d[6] = s[1]; + d[7] = s[0]; + } + return tmp; +} + +#define dbg(log_level, fmt, arg...) \ + do \ + { \ + dbg_time(fmt "\n", ##arg); \ + } while (0) + +static int sahara_tx_data(void *usb_handle, void *tx_buffer, size_t bytes_to_send) +{ + int need_zlp = 0; // zlp is not mandatory + return qusb_noblock_write(usb_handle, tx_buffer, bytes_to_send, bytes_to_send, 3000, need_zlp); +} + +int qusb_use_usbfs_interface(const void *handle); +static int sahara_rx_data(void *usb_handle, void *rx_buffer, size_t bytes_to_read) +{ + q_sahara_packet_h *command_packet_header = NULL; + size_t bytes_read = 0; + + const char *q_sahara_cmd_str[Q_SAHARA_NINETEEN] = { + "Q_SAHARA_ZERO", // = 0x00, + "Q_SAHARA_ONE", // = 0x01, // sent from target to host + "Q_SAHARA_TWO", // = 0x02, // sent from host to target + "Q_SAHARA_THREE", // = 0x03, // sent from target to host + "Q_SAHARA_FOUR", // = 0x04, // sent from target to host + "Q_SAHARA_FIVE", // = 0x05, // sent from host to target + "Q_SAHARA_SIX", // = 0x06, // sent from target to host + "Q_SAHARA_SEVEN", // = 0x07, // sent from host to target + "Q_SAHARA_EIGTH", // = 0x08, // sent from target to host + "Q_SAHARA_NINE", // = 0x09, // sent from target to host + "Q_SAHARA_TEN", // = 0x0A, // sent from host to target + "Q_SAHARA_ELEVEN", // = 0x0B, // sent from target to host + "Q_SAHARA_TWELEVE", // = 0x0C, // sent from host to target + "Q_SAHARA_THIRTEEN", // = 0x0D, // sent from host to target + "Q_SAHARA_FOURTEEN", // = 0x0E, // sent from target to host + "Q_SAHARA_FIFTEEN", // = 0x0F, // sent from host to target + "Q_SAHARA_SIXTEEN", // = 0x10, // sent from target to host + "Q_SAHARA_SEVENTEEN", // = 0x11, // sent from host to target + "Q_SAHARA_EIGHTEEN", // = 0x12, + }; + + if (0 == bytes_to_read) + { + if (qusb_use_usbfs_interface(usb_handle)) + { + bytes_read = qusb_noblock_read(usb_handle, rx_buffer, Q_SAHARA_RAW_BUF_SZ, 0, 5000); + if (bytes_read < sizeof(q_sahara_packet_h)) return 0; + } + else + { + bytes_read = + qusb_noblock_read(usb_handle, rx_buffer, sizeof(q_sahara_packet_h), 0, 5000); + if (bytes_read != sizeof(q_sahara_packet_h)) return 0; + } + + command_packet_header = (q_sahara_packet_h *)rx_buffer; + if (le_uint32(command_packet_header->q_cmd) < Q_SAHARA_NINETEEN) + { + dbg(LOG_EVENT, "<=== %s", q_sahara_cmd_str[le_uint32(command_packet_header->q_cmd)]); + + if (!qusb_use_usbfs_interface(usb_handle)) + { + bytes_read += qusb_noblock_read( + usb_handle, (uint8_t *)rx_buffer + sizeof(q_sahara_packet_h), + le_uint32(command_packet_header->q_len) - sizeof(q_sahara_packet_h), 0, 5000); + } + + if (bytes_read != (le_uint32(command_packet_header->q_len))) + { + dbg(LOG_INFO, "Read %zd bytes, Header indicates q_cmd %d and packet q_len %d bytes", + bytes_read, le_uint32(command_packet_header->q_cmd), + le_uint32(command_packet_header->q_len)); + return 0; + } + } + else + { + dbg(LOG_EVENT, "<=== SAHARA_CMD_UNKONOW_%d", le_uint32(command_packet_header->q_cmd)); + return 0; + } + } + else + { + bytes_read = qusb_noblock_read(usb_handle, rx_buffer, bytes_to_read, bytes_to_read, 5000); + } + + return 1; +} + +static int send_reset_command(void *usb_handle, void *tx_buffer) +{ + struct sahara_pkt *sahara_reset; + sahara_reset = (struct sahara_pkt *)tx_buffer; + sahara_reset->q_header.q_cmd = le_uint32(Q_SAHARA_SEVEN); + sahara_reset->q_header.q_len = + le_uint32(sizeof(sahara_reset->q_sahara_reset_packet) + sizeof(q_sahara_packet_h)); + + /* Send the Reset Request */ + dbg(LOG_EVENT, "SAHARA_RESET ===>"); + if (0 == + sahara_tx_data(usb_handle, tx_buffer, + sizeof(sahara_reset->q_sahara_reset_packet) + sizeof(q_sahara_packet_h))) + { + dbg(LOG_ERROR, "Sending RESET packet failed"); + return 0; + } + + return 1; +} + +static int send_done_packet(void *usb_handle, void *tx_buffer) +{ + struct sahara_pkt *sahara_done; + sahara_done = (struct sahara_pkt *)tx_buffer; + + sahara_done->q_header.q_cmd = le_uint32(Q_SAHARA_FIVE); + sahara_done->q_header.q_len = + le_uint32(sizeof(sahara_done->q_sahara_done_packet) + sizeof(q_sahara_packet_h)); + // Send the image data + dbg(LOG_EVENT, "Q_SAHARA_FIVE ===>"); + if (0 == sahara_tx_data(usb_handle, tx_buffer, + sizeof(sahara_done->q_sahara_done_packet) + sizeof(q_sahara_packet_h))) + { + dbg(LOG_ERROR, "Sending DONE packet failed"); + return 0; + } + return 1; +} + +static int start_image_transfer(void *usb_handle, void *tx_buffer, + const struct sahara_pkt *pr_sahara_pkt, FILE *file_handle) +{ + int retval = 0; + uint32_t bytes_read = 0, bytes_to_read_next; + uint32_t q_image_id = le_uint32(pr_sahara_pkt->q_sahara_read_packet_data.q_image_id); + uint32_t DataOffset = le_uint32(pr_sahara_pkt->q_sahara_read_packet_data.q_data_offset); + uint32_t DataLength = le_uint32(pr_sahara_pkt->q_sahara_read_packet_data.q_data_length); + + if (le_uint32(pr_sahara_pkt->q_header.q_cmd) == Q_SAHARA_EIGHTEEN) + { + q_image_id = le_uint64(pr_sahara_pkt->q_sahara_read_packet_data_64bit.q_image_id); + DataOffset = le_uint64(pr_sahara_pkt->q_sahara_read_packet_data_64bit.q_data_offset); + DataLength = le_uint64(pr_sahara_pkt->q_sahara_read_packet_data_64bit.q_data_length); + } + + dbg(LOG_INFO, "0x%08x 0x%08x 0x%08x", q_image_id, DataOffset, DataLength); + + if (fseek(file_handle, (long)DataOffset, SEEK_SET)) + { + dbg(LOG_INFO, "%d errno: %d (%s)", __LINE__, errno, strerror(errno)); + return 0; + } + + while (bytes_read < DataLength) + { + bytes_to_read_next = MIN((uint32_t)DataLength - bytes_read, Q_SAHARA_RAW_BUF_SZ); + retval = fread(tx_buffer, 1, bytes_to_read_next, file_handle); + + if (retval < 0) + { + dbg(LOG_ERROR, "file read failed: %s", strerror(errno)); + return 0; + } + + if ((uint32_t)retval != bytes_to_read_next) + { + dbg(LOG_ERROR, "Read %d bytes, but was asked for 0x%08x bytes", retval, DataLength); + return 0; + } + + /*send the image data*/ + if (0 == sahara_tx_data(usb_handle, tx_buffer, bytes_to_read_next)) + { + dbg(LOG_ERROR, "Tx Sahara Image Failed"); + return 0; + } + + bytes_read += bytes_to_read_next; + } + + return 1; +} + +static int send_hello_response(void *usb_handle, void *tx_buffer, + const struct sahara_pkt *sahara_hello) +{ + struct sahara_pkt *sahara_hello_resp; + sahara_hello_resp = (struct sahara_pkt *)tx_buffer; + + // Recieved hello, send the hello response + // Create a Hello request + sahara_hello_resp->q_header.q_cmd = le_uint32(Q_SAHARA_TWO); + sahara_hello_resp->q_header.q_len = le_uint32( + sizeof(sahara_hello_resp->q_sahara_hello_packet_response) + sizeof(q_sahara_packet_h)); + sahara_hello_resp->q_sahara_hello_packet_response.q_ver = + sahara_hello->q_sahara_hello_packet.q_ver; + sahara_hello_resp->q_sahara_hello_packet_response.q_ver_sup = + sahara_hello->q_sahara_hello_packet.q_ver_sup; + sahara_hello_resp->q_sahara_hello_packet_response.q_status = le_uint32(Q_SAHARA_STATUS_ZERO); + sahara_hello_resp->q_sahara_hello_packet_response.q_mode = + sahara_hello->q_sahara_hello_packet.q_mode; + sahara_hello_resp->q_sahara_hello_packet_response.q_reserve1 = le_uint32(1); + sahara_hello_resp->q_sahara_hello_packet_response.q_reserve2 = le_uint32(2); + sahara_hello_resp->q_sahara_hello_packet_response.q_reserve3 = le_uint32(3); + sahara_hello_resp->q_sahara_hello_packet_response.q_reserve4 = le_uint32(4); + sahara_hello_resp->q_sahara_hello_packet_response.q_reserve5 = le_uint32(5); + sahara_hello_resp->q_sahara_hello_packet_response.q_reserve6 = le_uint32(6); + + if (le_uint32(sahara_hello->q_sahara_hello_packet.q_mode) != Q_SAHARA_MODE_ZERO) + { + dbg(LOG_ERROR, "ERROR NOT Q_SAHARA_MODE_ZERO"); + sahara_hello_resp->q_sahara_hello_packet_response.q_mode = Q_SAHARA_MODE_ZERO; + } + + /*Send the Hello Resonse Request*/ + dbg(LOG_EVENT, "Q_SAHARA_TWO ===>"); + if (0 == sahara_tx_data(usb_handle, tx_buffer, + sizeof(sahara_hello_resp->q_sahara_hello_packet_response) + + sizeof(q_sahara_packet_h))) + { + dbg(LOG_ERROR, "Tx Sahara Data Failed "); + return 0; + } + + return 1; +} + +static int sahara_flash_all(void *usb_handle, void *tx_buffer, void *rx_buffer, FILE *file_handle) +{ + uint32_t q_image_id = 0; + struct sahara_pkt *pr_sahara_pkt; + + pr_sahara_pkt = (struct sahara_pkt *)rx_buffer; + + if (0 == sahara_rx_data(usb_handle, rx_buffer, 0)) + { + sahara_tx_data(usb_handle, tx_buffer, 1); + if (0 == sahara_rx_data(usb_handle, rx_buffer, 0)) return 0; + } + + if (le_uint32(pr_sahara_pkt->q_header.q_cmd) != Q_SAHARA_ONE) + { + dbg(LOG_ERROR, "Received a different q_cmd: %x while waiting for hello packet", + pr_sahara_pkt->q_header.q_cmd); + send_reset_command(usb_handle, rx_buffer); + return 0; + } + + if (0 == send_hello_response(usb_handle, tx_buffer, pr_sahara_pkt)) + { + dbg(LOG_ERROR, "send_hello_response failed\n"); + return 0; + } + + while (1) + { + if (0 == sahara_rx_data(usb_handle, rx_buffer, 0)) return 0; + + if (le_uint32(pr_sahara_pkt->q_header.q_cmd) == Q_SAHARA_THREE) + { + start_image_transfer(usb_handle, tx_buffer, pr_sahara_pkt, file_handle); + } + else if (le_uint32(pr_sahara_pkt->q_header.q_cmd) == Q_SAHARA_EIGHTEEN) + { + start_image_transfer(usb_handle, tx_buffer, pr_sahara_pkt, file_handle); + } + else if (le_uint32(pr_sahara_pkt->q_header.q_cmd) == Q_SAHARA_FOUR) + { + dbg(LOG_EVENT, "q_image_id = %d, q_status = %d", + le_uint32(pr_sahara_pkt->q_sahara_end_packet_image_tx.q_image_id), + le_uint32(pr_sahara_pkt->q_sahara_end_packet_image_tx.q_status)); + if (le_uint32(pr_sahara_pkt->q_sahara_end_packet_image_tx.q_status) == + Q_SAHARA_STATUS_ZERO) + { + q_image_id = le_uint32(pr_sahara_pkt->q_sahara_end_packet_image_tx.q_image_id); + send_done_packet(usb_handle, tx_buffer); + break; + } + else + { + return 0; + } + } + else if (le_uint32(pr_sahara_pkt->q_header.q_cmd) == Q_SAHARA_ONE) + { + continue; + } + else + { + dbg(LOG_ERROR, "Received an unknown q_cmd: %d ", + le_uint32(pr_sahara_pkt->q_header.q_cmd)); + send_reset_command(usb_handle, tx_buffer); + return 0; + } + } + + if (0 == sahara_rx_data(usb_handle, rx_buffer, 0)) return 0; + + dbg(LOG_INFO, "q_image_tx_status = %d", + le_uint32(pr_sahara_pkt->q_sahara_done_packet_response.q_image_tx_status)); + + if (Q_SAHARA_MODE_ZERO == + le_uint32(pr_sahara_pkt->q_sahara_done_packet_response.q_image_tx_status)) + { + if (q_image_id == 13) // prog_nand_firehose_9x07.mbn + return 1; + if (q_image_id == 7) // NPRG9x55.mbn + return 1; + if (q_image_id == 21) // sbl1.mbn, October 22 2020 2:12 PM, AG35CEVAR05A07T4G + return 1; + } + else if (Q_SAHARA_MODE_ONE == + le_uint32(pr_sahara_pkt->q_sahara_done_packet_response.q_image_tx_status)) + { + dbg(LOG_EVENT, "Successfully flash all images"); + return 1; + } + else + { + dbg(LOG_ERROR, "Received unrecognized q_status %d at Q_SAHARA_WAIT_FOUR state", + le_uint32(pr_sahara_pkt->q_sahara_done_packet_response.q_image_tx_status)); + return 0; + } + + return 0; +} + +int sahara_main(const char *firehose_dir, const char *firehose_mbn, void *usb_handle, + int edl_mode_05c69008) +{ + int retval = 0; + char full_path[512]; + FILE *file_handle; + void *tx_buffer; + void *rx_buffer; + + if (edl_mode_05c69008) + { + if (is_upgrade_fimeware_zip_7z) + { + snprintf(full_path, sizeof(full_path), "/tmp/%.240s", firehose_mbn); + dbg_time("%s full_path:%s\n", __func__, full_path); + } + else + { + snprintf(full_path, sizeof(full_path), "%.255s/%.240s", firehose_dir, firehose_mbn); + } + } + else + { + char *prog_nand_firehose_filename = NULL; + + if (is_upgrade_fimeware_zip_7z) + { + int i; + char prog_nand_firehose_filename_tmp[128] = {0}; + char prog_nand_firehose_filename_dir_tmp[256] = {0}; + + prog_nand_firehose_filename = (char *)malloc(256); + if (prog_nand_firehose_filename == NULL) + { + return ENOENT; + } + + for (i = 0; i < file_name_b.file_name_count; i++) + { + if ((strstr(file_name_b.file_backup_c[i].zip_file_name_backup, "NPRG9x") && + strstr(file_name_b.file_backup_c[i].zip_file_name_backup, ".mbn"))) + { + dbg_time("file_name_b.file_backup_c[i].zip_file_name_backup:%s\n", + file_name_b.file_backup_c[i].zip_file_name_backup); + dbg_time("file_name_b.file_backup_c[i].zip_file_dir_backup:%s\n", + file_name_b.file_backup_c[i].zip_file_dir_backup); + + if (strstr(file_name_b.file_backup_c[i].zip_file_dir_backup, "update/firehose")) + { + memmove(prog_nand_firehose_filename_tmp, + file_name_b.file_backup_c[i].zip_file_name_backup, + strlen(file_name_b.file_backup_c[i].zip_file_name_backup)); + memmove(prog_nand_firehose_filename_dir_tmp, + file_name_b.file_backup_c[i].zip_file_dir_backup, + strlen(file_name_b.file_backup_c[i].zip_file_dir_backup)); + break; + } + } + } + + if (prog_nand_firehose_filename_tmp[0] != '\0') + { + memset(zip_cmd_buf, 0, sizeof(zip_cmd_buf)); + if (is_upgrade_fimeware_only_zip) + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), + "unzip -o -q %.240s '*%.200s' -d /tmp/ > %s", firehose_dir, + prog_nand_firehose_filename_dir_tmp, ZIP_PROCESS_INFO); + } + else + { + snprintf(zip_cmd_buf, sizeof(zip_cmd_buf), "7z x %.240s -o/tmp/ %.200s > %s", + firehose_dir, prog_nand_firehose_filename_dir_tmp, ZIP_PROCESS_INFO); + } + dbg_time("%s zip_cmd_buf:%s\n", __func__, zip_cmd_buf); + if (-1 == system(zip_cmd_buf)) + { + dbg_time("%s system return error\n", __func__); + safe_free(prog_nand_firehose_filename); + return ENOENT; + } + usleep(1000); + + memmove(prog_nand_firehose_filename, prog_nand_firehose_filename_dir_tmp, 240); + dbg(LOG_INFO, "prog_nand_firehose_filename = %s", prog_nand_firehose_filename); + + snprintf(full_path, sizeof(full_path), "/tmp/%.240s", prog_nand_firehose_filename); + } + } + else + { + snprintf(full_path, sizeof(full_path), "%.255s/..", firehose_dir); + if (!qfile_find_file(full_path, "NPRG9x", ".mbn", &prog_nand_firehose_filename) && + !qfile_find_file(full_path, "NPRG9x", ".mbn", &prog_nand_firehose_filename)) + { + dbg(LOG_ERROR, "retrieve NPRG MBN failed."); + safe_free(prog_nand_firehose_filename); + return ENOENT; + } + dbg(LOG_INFO, "prog_nand_firehose_filename = %s", prog_nand_firehose_filename); + + snprintf(full_path, sizeof(full_path), "%.255s/../%.240s", firehose_dir, + prog_nand_firehose_filename); + } + + safe_free(prog_nand_firehose_filename); + } + + file_handle = fopen(full_path, "rb"); + if (file_handle == NULL) + { + dbg(LOG_INFO, "%s %d %s errno: %d (%s)", __func__, __LINE__, full_path, errno, + strerror(errno)); + return ENOENT; + } + + rx_buffer = malloc(Q_SAHARA_RAW_BUF_SZ); + tx_buffer = malloc(Q_SAHARA_RAW_BUF_SZ); + + if (NULL == rx_buffer || NULL == tx_buffer) + { + dbg(LOG_ERROR, "Failed to allocate sahara buffers"); + safe_free(rx_buffer); + safe_free(tx_buffer); + fclose(file_handle); + file_handle = NULL; + return ENOMEM; + } + + retval = sahara_flash_all(usb_handle, tx_buffer, rx_buffer, file_handle); + if (0 == retval) + { + dbg(LOG_ERROR, "Sahara protocol error"); + } + else + { + dbg(LOG_STATUS, "Sahara protocol completed"); + } + + safe_free(rx_buffer); + safe_free(tx_buffer); + fclose(file_handle); + file_handle = NULL; + + if (is_upgrade_fimeware_zip_7z) + { + unlink(full_path); + } + + if (retval) return 0; + + return __LINE__; +} diff --git a/application/qfirehose/src/sahara.h b/application/qfirehose/src/sahara.h new file mode 100644 index 0000000..7e467a3 --- /dev/null +++ b/application/qfirehose/src/sahara.h @@ -0,0 +1,94 @@ +/* + Copyright 2023 Quectel Wireless Solutions Co.,Ltd + + Quectel hereby grants customers of Quectel a license to use, modify, + distribute and publish the Software in binary form provided that + customers shall have no right to reverse engineer, reverse assemble, + decompile or reduce to source code form any portion of the Software. + Under no circumstances may customers modify, demonstrate, use, deliver + or disclose any portion of the Software in source code form. +*/ + +#ifndef SAHARA_H +#define SAHARA_H + +#define Q_SAHARA_RAW_BUF_SZ (4*1024) +#define Q_SAHARA_STATUS_ZERO 0x00 +#define Q_SAHARA_MODE_ZERO 0x00 +#define Q_SAHARA_MODE_ONE 0x01 + +#define Q_SAHARA_ZERO 0x00 +#define Q_SAHARA_ONE 0x01 +#define Q_SAHARA_TWO 0x02 +#define Q_SAHARA_THREE 0x03 +#define Q_SAHARA_FOUR 0x04 +#define Q_SAHARA_FIVE 0x05 +#define Q_SAHARA_SEVEN 0x07 +#define Q_SAHARA_EIGHTEEN 0x12 +#define Q_SAHARA_NINETEEN 0x13 + +typedef struct +{ + uint32_t q_cmd; + uint32_t q_len; +} q_sahara_packet_h; + +struct sahara_pkt +{ + q_sahara_packet_h q_header; + + union + { + struct + { + uint32_t q_ver; + uint32_t q_ver_sup; + uint32_t q_cmd_packet_len; + uint32_t q_mode; + } q_sahara_hello_packet; + struct + { + uint32_t q_ver; + uint32_t q_ver_sup; + uint32_t q_status; + uint32_t q_mode; + uint32_t q_reserve1; + uint32_t q_reserve2; + uint32_t q_reserve3; + uint32_t q_reserve4; + uint32_t q_reserve5; + uint32_t q_reserve6; + } q_sahara_hello_packet_response; + struct + { + uint32_t q_image_id; + uint32_t q_data_offset; + uint32_t q_data_length; + } q_sahara_read_packet_data; + struct + { + uint32_t q_image_id; + uint32_t q_status; + } q_sahara_end_packet_image_tx; + struct + { + } q_sahara_done_packet; + struct + { + uint32_t q_image_tx_status; + } q_sahara_done_packet_response; + struct + { + uint64_t q_image_id; + uint64_t q_data_offset; + uint64_t q_data_length; + } q_sahara_read_packet_data_64bit; + struct + { + } q_sahara_reset_packet; + struct + { + } q_sahara_reset_packet_response; + }; +}; +#endif diff --git a/application/qfirehose/src/stream_download_protocol.c b/application/qfirehose/src/stream_download_protocol.c new file mode 100644 index 0000000..1b10dd4 --- /dev/null +++ b/application/qfirehose/src/stream_download_protocol.c @@ -0,0 +1,826 @@ +/* + Copyright 2023 Quectel Wireless Solutions Co.,Ltd + + Quectel hereby grants customers of Quectel a license to use, modify, + distribute and publish the Software in binary form provided that + customers shall have no right to reverse engineer, reverse assemble, + decompile or reduce to source code form any portion of the Software. + Under no circumstances may customers modify, demonstrate, use, deliver + or disclose any portion of the Software in source code form. +*/ + +#include "usb_linux.h" +#include "hostdl_packet.h" + +#define true (1 == 1) +#define false (1 != 1) + +#define MAX_SEND_BUFFER_SIZE 1280 +#define MAX_RECEIVE_BUFFER_SIZE 1280 +unsigned char g_Transmit_Buffer[MAX_SEND_BUFFER_SIZE]; +int g_Transmit_Length; + +unsigned char g_Receive_Buffer[MAX_RECEIVE_BUFFER_SIZE]; +int g_Receive_Bytes; + +static void *stream_usb_handle; + +static void dump_buffer(unsigned char *buff, int len) +{ + int i = 0; + + dbg_time("dump buffer: %d bytes\n", len); + for (i = 0; i < len; i++) + { + dbg_time("%02x ", buff[i]); + } + dbg_time("\nend\n"); +} + +#define CRC_16_L_SEED 0xFFFF +#define CRC_TAB_SIZE 256 /* 2^CRC_TAB_BITS */ +#define CRC_16_L_POLYNOMIAL 0x8408 + +static const uint16_t crc_16_l_table[CRC_TAB_SIZE] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, + 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399, + 0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50, + 0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e, + 0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5, + 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693, + 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a, + 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710, + 0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df, + 0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595, + 0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c, + 0x3de3, 0x2c6a, 0x1ef1, 0x0f78}; + +unsigned short crc_16_l_calc(unsigned char *buf_ptr, int len) +{ + int data, crc_16; + for (crc_16 = CRC_16_L_SEED; len >= 8; len -= 8, buf_ptr++) + { + crc_16 = crc_16_l_table[(crc_16 ^ *buf_ptr) & 0x00ff] ^ (crc_16 >> 8); + } + if (len != 0) + { + data = ((int)(*buf_ptr)) << (16 - 8); + + while (len-- != 0) + { + if (((crc_16 ^ data) & 0x01) != 0) + { + crc_16 >>= 1; + crc_16 ^= CRC_16_L_POLYNOMIAL; + } + else + { + crc_16 >>= 1; + } + + data >>= 1; + } + } + return (~crc_16); +} + +void compute_reply_crc() +{ + unsigned short crc = crc_16_l_calc(g_Transmit_Buffer, g_Transmit_Length * 8); + g_Transmit_Buffer[g_Transmit_Length] = crc & 0xFF; + g_Transmit_Buffer[g_Transmit_Length + 1] = crc >> 8; + g_Transmit_Length += 2; +} + +static void compose_packet(unsigned char cmd, unsigned char *parameter, uint32_t parameter_len, + unsigned char *data, uint32_t data_len) +{ + uint32_t i; + + g_Transmit_Buffer[0] = cmd; + if (parameter == NULL) parameter_len = 0; + if (data == NULL) data_len = 0; + for (i = 0; i < parameter_len; i++) + { + g_Transmit_Buffer[1 + i] = parameter[i]; + } + for (i = 0; i < data_len; i++) + { + g_Transmit_Buffer[1 + parameter_len + i] = data[i]; + } + g_Transmit_Length = 1 + parameter_len + data_len; + g_Transmit_Buffer[g_Transmit_Length] = 0; +} + +static unsigned char stream_tx_buf[1280]; +#define CHECK_FOR_DATA() \ + do \ + { \ + } while (0) +#define TRANSMIT_BYTE(_byte) \ + do \ + { \ + stream_tx_buf[j++] = _byte; \ + } while (0) + +static int send_packet(int flag) +{ + int i; + int ch; + int j; + + j = 0; + + CHECK_FOR_DATA(); + + /* Since we don't know how long it's been. */ + if (!!flag) + { + TRANSMIT_BYTE(0x7E); + } + + for (i = 0; i < g_Transmit_Length; i++) + { + /* we only need to check once every 31 characters, since RX and TX + * run at about the same speed, and our RX FIFO is 64 characters + */ + if ((i & 31) == 31) CHECK_FOR_DATA(); + + ch = g_Transmit_Buffer[i]; + + if (ch == 0x7E || ch == 0x7D) + { + TRANSMIT_BYTE(0x7D); + TRANSMIT_BYTE(0x20 ^ ch); /*lint !e734 */ + } + else + { + TRANSMIT_BYTE(ch); /*lint !e734 */ + } + } + + CHECK_FOR_DATA(); + TRANSMIT_BYTE(0x7E); + +#if 0 + /* Hack for USB protocol. If we have an exact multiple of the USB frame + * size, then the last frame will not be sent out. The USB standard says + * that a "short packet" needs to be sent to flush the data. Two flag + * characters can serve as the short packet. Doing it this way, we only + * perform this test once on every entire packet from the target, so the + * over head is not too much. + */ + if ((j%512) == 0) + { + TRANSMIT_BYTE (0x7E); + TRANSMIT_BYTE (0x7E); + } +#endif + + return (qusb_noblock_write(stream_usb_handle, stream_tx_buf, j, j, 3000, 1) == j) ? 0 : -1; +} + +static int remove_escape_hdlc_flag(unsigned char *buffer, int len) +{ + int i = 0; + int index = 0; + int escape = 0; + // dump_buffer(buffer, len); + if (len == 0) return 0; + // ignore the first HDLC FLAG bytes + while (buffer[i] == 0x7e) + { + i++; + } + // all bytes is HDLC FLAG + if (i == len) return 0; + for (; i < len; i++) + { + if (buffer[i] == 0x7D) + { + escape = 1; + continue; + } + if (escape == 1) + { + escape = 0; + buffer[i] ^= 0x20; + } + buffer[index++] = buffer[i]; + } + buffer[index] = 0; + // dump_buffer(buffer, index); + return index; +} + +static int receive_packet(void) +{ + int bytesread = 0; + unsigned char *buff = g_Receive_Buffer; + if (buff == NULL) + { + return -1; + } + + int idx = 0; + do + { + bytesread = + qusb_noblock_read(stream_usb_handle, &buff[idx], MAX_RECEIVE_BUFFER_SIZE, 0, 3000); + if (bytesread == 0) + { + // timeout may be error + dbg_time("%s timeout\n", __FUNCTION__); + break; + } + // dump_buffer(&buff[idx], bytesread); + idx += bytesread; + if (buff[idx - 1] == 0x7e) + { + // check the packet whether valid. + g_Receive_Bytes = remove_escape_hdlc_flag(buff, idx); + if (g_Receive_Bytes == 0) + { + continue; + } + else + { + return 1; + } + } + } while (1); + + return 0; +} + +static int handle_hello(void) +{ + static const char host_header[] = "QCOM fast download protocol host"; + // static const char target_header[] = "QCOM fast download protocol targ"; + // char string1[64]; + // int size; + int err; + dbg_time("%s\n", __func__); + + memset(&g_Transmit_Buffer[0], 0, sizeof(g_Transmit_Buffer)); + g_Transmit_Buffer[HELLO_CMD_OFFSET] = HELLO_REQ; + memcpy(&g_Transmit_Buffer[HELLO_MAGIC_NUM_OFFSET], host_header, 32); + g_Transmit_Buffer[HELLO_MAX_VER_OFFSET] = STREAM_DLOAD_MAX_VER; + g_Transmit_Buffer[HELLO_MIN_VER_OFFSET] = STREAM_DLOAD_MIN_VER; + g_Transmit_Buffer[HELLO_MAX_DATA_SZ_1_OFFSET] = 0; + g_Transmit_Length = 36; + + compute_reply_crc(); + send_packet(1); + + int timeout = 5; + do + { + err = receive_packet(); + if (err == 1) + { + switch (g_Receive_Buffer[0]) + { + case 0x02: return 1; + case 0x0d: continue; + default: + // dump_buffer(g_Receive_Buffer, 64); + return 0; + } + } + else if (err == -1) + { + dbg_time("error = %d, strerr = %s\n", errno, strerror(errno)); + return 0; + } + timeout--; + } while (timeout); + + return 0; +} + +static int handle_security_mode(unsigned char trusted) +{ + dbg_time("%s trusted = %d\n", __func__, trusted); + compose_packet(0x17, &trusted, 1, NULL, 0); + compute_reply_crc(); + send_packet(1); + int timeout = 5; + do + { + if (receive_packet() == 1) + { + switch (g_Receive_Buffer[0]) + { + case 0x18: return 1; + default: return 0; + } + } + else + { + timeout--; + if (timeout == 0) + { + dbg_time("%s timeout\n", __FUNCTION__); + break; // return 0; -Werror,-Wunreachable-code-return + } + } + } while (1); + return 0; +} +/* +set download flag in module, quectel custom command, +if flag : reboot, module will enter DM +if not flag: reboot normal +*/ +static int handle_quectel_download_flag(unsigned char mode) +{ + // byte mode = 1; + compose_packet(0x60, &mode, 1, NULL, 0); + compute_reply_crc(); + send_packet(1); + int timeout = 5; + do + { + if (receive_packet() == 1) + { + switch (g_Receive_Buffer[0]) + { + case 0x61: + switch (g_Receive_Buffer[1]) + { + case 0x00: return 1; + default: return 0; + } + break; + case 0x0E: dbg_time("Invalid command"); return 2; + default: dump_buffer(g_Receive_Buffer, 64); return 0; + } + } + else + { + timeout--; + if (timeout == 0) + { + dbg_time("%s timeout\n", __FUNCTION__); + return 0; + } + } + } while (1); +} + +static const char *stream_firehose_dir; +static int stread_fread(const char *filename, void **pp_filebuf) +{ + int filesize = 0; + FILE *fp; + char fullpath[MAX_PATH * 2]; + + snprintf(fullpath, sizeof(fullpath), "%.240s/../%.240s", stream_firehose_dir, filename); + fp = fopen(fullpath, "rb"); + if (fp == NULL) + { + dbg_time("fail to fope %s, errno: %d (%s)\n", fullpath, errno, strerror(errno)); + return 0; + } + + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + + *pp_filebuf = malloc(filesize); + if (pp_filebuf == NULL) + { + dbg_time("fail to malloc %d, errno: %d (%s)\n", filesize, errno, strerror(errno)); + if (fp) + { + fclose(fp); + fp = NULL; + } + return 0; + } + + fseek(fp, 0, SEEK_SET); + filesize = fread(*pp_filebuf, 1, filesize, fp); + fclose(fp); + + dbg_time("%s filename=%s, filesize=%d\n", __func__, filename, filesize); + return filesize; +} + +static int handle_parti_tbl(unsigned char override) +{ + int timeout = 5; + int filesize; + void *filebuf; + const char *partition_path = "partition.mbn"; + dbg_time("%s override = %d\n", __func__, override); + + filesize = stread_fread(partition_path, &filebuf); + if (filesize <= 0) + { + if (filebuf) + { + free(filebuf); + filebuf = NULL; + } + return 0; + } + + compose_packet(0x19, &override, 1, filebuf, filesize); + compute_reply_crc(); + send_packet(1); + free(filebuf); + + do + { + if (receive_packet() == 1) + { + dbg_time("handle_parti_tbl command = %02x, status = %02x\n", g_Receive_Buffer[0], + g_Receive_Buffer[1]); + switch (g_Receive_Buffer[0]) + { + case 0x1a: + switch (g_Receive_Buffer[1]) + { + case 0x00: return 1; + case 0x01: // 0x1 this means that the original partition is different from + // the current partition,try to send partition + return 0; + case 0x02: // 0x2 Partition table format not recognized, does not accept + // override + return 0; + case 0x03: // 0x3 Erase operation failed + return 0; + break; + default: return 0; + } + default: return 0; + } + } + else + { + timeout--; + if (timeout == 0) + { + dbg_time("%s timeout\n", __FUNCTION__); + return 0; + } + } + } while (1); +} + +static int handle_reset(void) +{ + dbg_time("%s\n", __func__); + compose_packet(0x0b, NULL, 0, NULL, 0); + compute_reply_crc(); + send_packet(1); +#if 1 + return 1; +#else + int timeout = 5; + do + { + if (receive_packet() == 1) + { + switch (g_Receive_Buffer[0]) + { + case 0x0c: return 1; + case 0x0d: continue; + default: dump_buffer(g_Receive_Buffer, 64); return 0; + } + } + else + { + timeout--; + if (timeout == 0) + { + dbg_time("%s timeout\n", __FUNCTION__); + return 0; + } + } + } while (1); +#endif +} + +/******pkt_open_multi_image*******/ + +static void pkt_open_multi_image(unsigned char mode, unsigned char *data, uint32_t size) +{ + compose_packet(0x1b, &mode, 1, data, size); + compute_reply_crc(); +} + +static int handle_openmulti(uint32_t size, unsigned char *data) +{ + int timeout = 5; + unsigned char mode = 0x0e; + + pkt_open_multi_image(mode, data, size); + send_packet(1); + do + { + if (receive_packet() == 1) + { + switch (g_Receive_Buffer[0]) + { + case 0x1c: return 1; + case 0x0d: continue; + default: return 0; + } + } + else + { + timeout--; + if (timeout == 0) + { + dbg_time("%s timeout\n", __FUNCTION__); + break; + } + } + } while (1); + return 0; +} + +/******pkt_write_multi_image*******/ +static void pkt_write_multi_image(uint32_t addr, unsigned char *data, uint16_t size) +{ + unsigned char parameter[4] = {(unsigned char)(addr)&0xff, (unsigned char)(addr >> 8) & 0xff, + (unsigned char)(addr >> 16) & 0xff, + (unsigned char)(addr >> 24) & 0xff}; + compose_packet(0x07, parameter, 4, data, size); + compute_reply_crc(); +} + +static int handle_write(unsigned char *data, uint32_t size) +{ + // uint32_t total_size; + uint32_t addr = 0; + uint32_t writesize; + uint32_t buffer_size = 1024; + // int loop = 1; + int retry_cnt = 3; // if send failed,send again + int ret; + + // total_size = size; + while (size) + { + writesize = (size < buffer_size) ? size : buffer_size; + + pkt_write_multi_image(addr, data, writesize); + start_send_packet: + ret = send_packet(1); + if (0 != ret) + { + dbg_time("io read/write failed\n"); + return 0; + } + if (receive_packet() == 1) + { + switch (g_Receive_Buffer[0]) + { + case 0x08: + size -= writesize; + addr += writesize; + // retry_cnt=5; + break; + default: + goto retry_send_packet; + // return 0; + } + } + else + { + retry_send_packet: + retry_cnt--; + if (retry_cnt > 0) + { + goto start_send_packet; + } + else + { + dbg_time("value is [0x%02x]", g_Receive_Buffer[0]); + return 0; + } + } + } + + return 1; +} +/******PARTITION*******/ +static int handle_close(void) +{ + int timeout = 5; + compose_packet(0x15, NULL, 0, NULL, 0); + compute_reply_crc(); + send_packet(1); + + do + { + if (receive_packet() == 1) + { + switch (g_Receive_Buffer[0]) + { + case 0x16: return 1; + default: return 0; + } + } + else + { + timeout--; + if (timeout == 0) + { + dbg_time("%s timeout\n", __FUNCTION__); + break; + } + } + } while (1); + return 0; +} + +static int do_flash_mbn(const char *partion, const char *filepath) +{ + int result = false; + void *filebuf = NULL; + int filesize = 0; + + dbg_time("%s %s\n", __func__, partion); + + if (filepath) + { + filesize = stread_fread(filepath, &filebuf); + if (filesize <= 0) + { + if (filebuf) + { + free(filebuf); + filebuf = NULL; + } + return 0; + } + } + else + { + filesize = 4 * 1024; + filebuf = (unsigned char *)malloc(filesize); + if (filebuf == NULL) + { + return 0; + } + + memset(filebuf, 0x00, filesize); + } + + result = handle_openmulti(strlen(partion) + 1, (unsigned char *)partion); + if (result == false) + { + dbg_time("%s open failed\n", partion); + goto __fail; + } + + dbg_time("sending '%s' (%dKB)\n", partion, (int)(filesize / 1024)); + + result = handle_write(filebuf, filesize); + if (result == false) + { + dbg_time("%s download failed\n", partion); + goto __fail; + } + + result = handle_close(); + if (result == false) + { + dbg_time("%s close failed.\n", partion); + goto __fail; + } + + dbg_time("OKAY\n"); + +__fail: + free(filebuf); + + return result; +} + +int stream_download(const char *firehose_dir, void *usb_handle, unsigned qusb_zlp_mode) +{ + (void)qusb_zlp_mode; + stream_usb_handle = usb_handle; + stream_firehose_dir = firehose_dir; + + if (handle_hello() == false) + { + dbg_time("Send hello command fail\n"); + return false; + } + + /* + hello packet will set dload flag in module, when upgrade interrup, restart module,module will + enter dm(quectel sbl) + */ + if (handle_security_mode(1) == false) + { + dbg_time("Send trust command fail\n"); + return false; + } + + if (handle_parti_tbl(0) == false) + { + dbg_time("----------------------------------\n"); + dbg_time("Detect partition mismatch.\n"); + dbg_time("Download parition with override.\n"); + dbg_time("----------------------------------\n"); + + if (handle_parti_tbl(1) == false) + { + dbg_time("override failed. \n"); + return false; + } + + /* + partition is not match, the download flag will be clear, so set it again, reset will clear + it + */ + if (handle_quectel_download_flag(1) == false) + { + dbg_time("Set Quectel download flag failed\n"); + } + else + { + dbg_time("Set Quectel download flag successfully\n"); + } + } + +#if 1 + if (do_flash_mbn("0:SBL", "sbl1.mbn") == false) + { + return false; + } +#endif + + if (handle_reset() == false) + { + dbg_time("Send reset command failed\n"); + return false; + } + + dbg_time("%s successful\n", __func__); + + return true; +} + +// retrieve module soft revision + +typedef struct +{ + unsigned char cmd_code; + unsigned char version; + unsigned char reserved[2]; + unsigned char msm[4]; + unsigned char mobile_modle_number[4]; + unsigned char mobile_software_revision[1]; +} __attribute__((packed)) extended_build_id_response_t; + +int retrieve_soft_revision(void *usb_handle, uint8_t *mobile_software_revision, unsigned length) +{ + /* + 80-v1294-1_yyd_serial_interface_control_document_(icd)_for_cdma_dual-mode_subscriber_station_data + 3.4.122 Extended Build ID + */ + uint8_t req1[] = {0x7E, 0x7C, 0x93, 0x49, 0x7E}; + int ret; + uint8_t *rx_buff = malloc(2048); + + memset(mobile_software_revision, 0x00, length); + + if (rx_buff == NULL) return 0; + + ret = qusb_noblock_write(usb_handle, req1, sizeof(req1), sizeof(req1), 1000, 0); + if (ret > 0) + { + ret = qusb_noblock_read(usb_handle, rx_buff, 2048, 1, 3000); + if (ret > 0) + { + if (rx_buff[0] == 0x7C && rx_buff[ret - 1] == 0x7E) + { + extended_build_id_response_t *rsp = (extended_build_id_response_t *)rx_buff; + (void)length; + memcpy(mobile_software_revision, rsp->mobile_software_revision, + strlen((const char *)rsp->mobile_software_revision)); + } + } + } + + free(rx_buff); + return (mobile_software_revision[0] != '\0'); +} diff --git a/application/qfirehose/src/usb2tcp.c b/application/qfirehose/src/usb2tcp.c new file mode 100644 index 0000000..6b8ce7e --- /dev/null +++ b/application/qfirehose/src/usb2tcp.c @@ -0,0 +1,381 @@ +/* + Copyright 2023 Quectel Wireless Solutions Co.,Ltd + + Quectel hereby grants customers of Quectel a license to use, modify, + distribute and publish the Software in binary form provided that + customers shall have no right to reverse engineer, reverse assemble, + decompile or reduce to source code form any portion of the Software. + Under no circumstances may customers modify, demonstrate, use, deliver + or disclose any portion of the Software in source code form. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) +#include +#else +#include +#endif +//#include +#include "usb_linux.h" +#include //for __BYTE_ORDER +char *inet_ntoa(struct in_addr in); + +#define dprintf dbg_time + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define MAX_USBFS_BULK_IN_SIZE (4 * 1024) +#define MAX_USBFS_BULK_OUT_SIZE (16 * 1024) + +static uint32_t cpu_to_le32(uint32_t v32) +{ + uint32_t tmp = v32; +#if __BYTE_ORDER == __LITTLE_ENDIAN +#else + unsigned char *s = (unsigned char *)(&v32); + unsigned char *d = (unsigned char *)(&tmp); + d[0] = s[3]; + d[1] = s[2]; + d[2] = s[1]; + d[3] = s[0]; +#endif + return tmp; +} +#define le32_to_cpu(_v32) cpu_to_le32(_v32) + +static int qusb_control[2]; + +static int noblock_full_read(int fd, void *pbuf, ssize_t size) +{ + ssize_t cur = 0; + + while (cur < size) + { + ssize_t ret = read(fd, (char *)pbuf + cur, size - cur); + + if (ret > 0) + cur += ret; + else if (ret < 0 && errno == EAGAIN) + { + struct pollfd pollfds[] = {{fd, POLLIN, 0}}; + poll(pollfds, 1, -1); + if (pollfds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) break; + } + else + { + dprintf("fd=%d read=%zd, errno: %d (%s)\n", fd, ret, errno, strerror(errno)); + break; + } + } + + if (cur != size) + { + dprintf("%s fd=%d cur=%zd, size=%zd\n", __func__, fd, cur, size); + } + + return cur; +} + +static ssize_t noblock_full_write(int fd, const void *pbuf, ssize_t size) +{ + ssize_t cur = 0; + + while (cur < size) + { + ssize_t ret = write(fd, (char *)pbuf + cur, size - cur); + if (ret > 0) + cur += ret; + else if (ret <= 0 && errno == EAGAIN) + { + struct pollfd pollfds[] = {{fd, POLLOUT, 0}}; + poll(pollfds, 1, -1); + if (pollfds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) break; + } + else + { + dprintf("fd=%d write=%zd, errno: %d (%s)\n", fd, ret, errno, strerror(errno)); + break; + } + } + + if (cur != size) + { + dprintf("%s fd=%d cur=%zd, size=%zd\n", __func__, fd, cur, size); + } + + return cur; +} + +static void *usb_bulk_read_thread(void *arg) +{ + const void *usb_handle = arg; + void *buf = malloc(MAX_USBFS_BULK_IN_SIZE); + int fd = qusb_control[1]; + + if (buf == NULL) return NULL; + + while (usb_handle) + { + int count = qusb_noblock_read(usb_handle, buf, MAX_USBFS_BULK_IN_SIZE, 1, 30000); + + if (count > 0) + { + count = write(fd, buf, count); + count = read(fd, buf, 32); // wait usb2tcp_main read + if (count <= 0) + { + dprintf("read=%d\n", count); + break; + } + } + else if (count <= 0) + { + break; + } + } + + close(fd); + free(buf); + return NULL; +} + +static int qusb_open(const void *usb_handle) +{ + int fd = -1; + pthread_t thread_id; + + pthread_attr_t usb_thread_attr; + pthread_attr_init(&usb_thread_attr); + pthread_attr_setdetachstate(&usb_thread_attr, PTHREAD_CREATE_DETACHED); + + socketpair(AF_LOCAL, SOCK_STREAM, 0, qusb_control); + pthread_create(&thread_id, &usb_thread_attr, usb_bulk_read_thread, (void *)usb_handle); + + fd = qusb_control[0]; + + // pthread_attr_destroy(&usb_thread_attr); //aaron 2023.07.27 + return fd; +} + +static ssize_t qusb_read(int fd, void *pbuf, size_t size) { return read(fd, pbuf, size); } + +static int create_tcp_server(int socket_port) +{ + int sockfd = -1; + int reuse_addr = 1; + struct sockaddr_in sockaddr; + + dprintf("%s tcp_port=%d\n", __func__, socket_port); + /*Create server socket*/ + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd <= 0) return sockfd; + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); + sockaddr.sin_port = htons(socket_port); + + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); + if (bind(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) + { + close(sockfd); + dprintf("%s bind %d errno: %d (%s)\n", __func__, socket_port, errno, strerror(errno)); + return -1; + } + + return sockfd; +} + +static int wait_client_connect(int server_fd) +{ + int client_fd = -1; + unsigned char addr[128]; + socklen_t alen = sizeof(addr); + + dprintf("%s\n", __func__); + listen(server_fd, 1); + client_fd = accept(server_fd, (struct sockaddr *)addr, &alen); + if (client_fd <= 0) return client_fd; + + if (client_fd > 0) + { + struct sockaddr_in *addr_in = (struct sockaddr_in *)addr; + dprintf("clientfd = %d %s:%d connect\n", client_fd, inet_ntoa(addr_in->sin_addr), + addr_in->sin_port); + } + + return client_fd; +} + +int usb2tcp_main(const void *usb_handle, int tcp_port, unsigned qusb_zlp_mode) +{ + void *pbuf = malloc(MAX_USBFS_BULK_OUT_SIZE); + int server_fd = -1, client_fd = -1, usb_fd = -1, size = -1; + TLV_USB tlv_usb; + + if (pbuf == NULL) return -1; + + server_fd = create_tcp_server(tcp_port); + dprintf("server_fd=%d\n", server_fd); + if (server_fd <= 0) + { + dprintf("Fail create_tcp_server\n"); + goto _out; + } + + if (client_fd <= 0) + { + client_fd = wait_client_connect(server_fd); + if (client_fd < 0) + { + dprintf("Fail wait_client_connect\n"); + goto _out; + } + } + + usb_fd = qusb_open(usb_handle); + dprintf("usb_fd = %d\n", usb_fd); + + tlv_usb.tag = cpu_to_le32(Q_USB2TCP_VERSION); + tlv_usb.length = cpu_to_le32(12); + tlv_usb.idVendor = cpu_to_le32(0x05c6); + tlv_usb.idProduct = cpu_to_le32(0x9008); + tlv_usb.interfaceNum = cpu_to_le32(1); + if (write(client_fd, &tlv_usb, sizeof(tlv_usb)) == -1) + { + }; + + fcntl(usb_fd, F_SETFL, fcntl(usb_fd, F_GETFL) | O_NONBLOCK); + fcntl(client_fd, F_SETFL, fcntl(client_fd, F_GETFL) | O_NONBLOCK); + + while (usb_fd > 0 && client_fd > 0) + { + struct pollfd pollfds[] = {{usb_fd, POLLIN, 0}, {client_fd, POLLIN, 0}}; + int ne, ret, nevents = sizeof(pollfds) / sizeof(pollfds[0]); + + do + { + ret = poll(pollfds, nevents, -1); + } while (ret < 0 && errno == EINTR); + + if (ret <= 0) + { + dprintf("%s poll=%d, errno: %d (%s)\n", __func__, ret, errno, strerror(errno)); + goto _hangup; + } + + if (pollfds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) + { + dprintf("%s poll usb_fd = %d, revents = %04x\n", __func__, usb_fd, pollfds[0].revents); + goto _hangup; + } + + if (pollfds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) + { + dprintf("%s poll client_fd = %d, revents = %04x\n", __func__, client_fd, + pollfds[1].revents); + goto _hangup; + } + + for (ne = 0; ne < nevents; ne++) + { + int fd = pollfds[ne].fd; + TLV tlv = {Q_USB2TCP_VERSION, 0}; + + if ((pollfds[ne].revents & POLLIN) == 0) continue; + + if (fd == usb_fd) + { + size = qusb_read(usb_fd, pbuf, MAX_USBFS_BULK_IN_SIZE); + if (size <= 0) + { + dprintf("usb_fd=%d read=%d, errno: %d (%s)\n", fd, size, errno, + strerror(errno)); + goto _hangup; + ; + } + if (write(usb_fd, pbuf, 1) == -1) + { + }; // wakeup usb_bulk_read_thread + + tlv.tag = cpu_to_le32(Q_USB2TCP_VERSION); + tlv.length = cpu_to_le32(size); + if (sizeof(tlv) != noblock_full_write(client_fd, &tlv, sizeof(tlv))) + { + goto _hangup; + break; + } + + if (size != noblock_full_write(client_fd, pbuf, size)) + { + goto _hangup; + break; + } + } + else if (fd == client_fd) + { + size = noblock_full_read(client_fd, &tlv, sizeof(tlv)); + if (size != sizeof(tlv)) + { + dprintf("client_fd=%d read=%d, errno: %d (%s)\n", fd, size, errno, + strerror(errno)); + goto _hangup; + } + + if (le32_to_cpu(tlv.tag) != Q_USB2TCP_VERSION) + { + break; + } + + size = le32_to_cpu(tlv.length); + if (size != noblock_full_read(client_fd, pbuf, size)) + { + goto _hangup; + break; + } + qusb_noblock_write(usb_handle, pbuf, size, size, 3000, qusb_zlp_mode); + } + } + } + +_hangup: + if (usb_fd > 0) + { + close(usb_fd); + usb_fd = -1; + } + if (client_fd > 0) + { + close(client_fd); + client_fd = -1; + } + +_out: + if (server_fd > 0) + { + close(server_fd); + server_fd = -1; + } + + free(pbuf); + return 0; +} diff --git a/application/qfirehose/src/usb_linux.c b/application/qfirehose/src/usb_linux.c new file mode 100644 index 0000000..a2f62c4 --- /dev/null +++ b/application/qfirehose/src/usb_linux.c @@ -0,0 +1,1657 @@ +/* + Copyright 2023 Quectel Wireless Solutions Co.,Ltd + + Quectel hereby grants customers of Quectel a license to use, modify, + distribute and publish the Software in binary form provided that + customers shall have no right to reverse engineer, reverse assemble, + decompile or reduce to source code form any portion of the Software. + Under no circumstances may customers modify, demonstrate, use, deliver + or disclose any portion of the Software in source code form. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) +#include +#else +#include +#endif +#include +#include +#include +#include "usb_linux.h" + +int edl_pcie_mhifd = -1; +int switch_to_edl_mode(void *usb_handle); + +extern uint32_t inet_addr(const char *); + +#define MAX_USBFS_BULK_IN_SIZE (4 * 1024) +#define EC20_MAX_INF 4 +#define MKDEV(__ma, __mi) (((__ma & 0xfff) << 8) | (__mi & 0xff) | ((__mi & 0xfff00) << 12)) + +struct quectel_usb_device +{ + char devname[64]; + int desc; + int ttyfd; + int idVendor; + int idProduct; + uint8_t bNumInterfaces; + uint8_t intr_ep[EC20_MAX_INF]; + uint8_t bulk_ep_in[EC20_MAX_INF]; + uint8_t bulk_ep_out[EC20_MAX_INF]; + int wMaxPacketSize[EC20_MAX_INF]; + int control[EC20_MAX_INF][2]; +}; + +static struct quectel_usb_device quectel_9x07; +static int tcp_socket_fd = -1; +static int usb_dm_interface = 0; + +typedef struct +{ + char diag[32]; + char edl[32]; + char bhi[32]; +} pcie_port_classification; + +typedef struct +{ + pcie_port_classification pcie_port_one[10]; + int pcie_port_number; +} pcie_port; + +static pcie_port pcie_port_defult = {{{"/dev/mhi_DIAG", "/dev/mhi_EDL", "/dev/mhi_BHI"}, + {"/dev/mhi_DIAG1", "/dev/mhi_EDL1", "/dev/mhi_BHI1"}, + {"/dev/mhi_DIAG2", "/dev/mhi_EDL2", "/dev/mhi_BHI2"}, + {"/dev/mhi_DIAG3", "/dev/mhi_EDL3", "/dev/mhi_BHI3"}, + {"/dev/mhi_DIAG4", "/dev/mhi_EDL4", "/dev/mhi_BHI4"}, + {"/dev/mhi_DIAG5", "/dev/mhi_EDL5", "/dev/mhi_BHI5"}, + {"/dev/mhi_DIAG6", "/dev/mhi_EDL6", "/dev/mhi_BHI6"}, + {"/dev/mhi_DIAG7", "/dev/mhi_EDL7", "/dev/mhi_BHI7"}, + {"/dev/mhi_DIAG8", "/dev/mhi_EDL8", "/dev/mhi_BHI8"}, + {"/dev/mhi_DIAG9", "/dev/mhi_EDL9", "/dev/mhi_BHI9"}}, + 10}; + +static int strStartsWith(const char *line, const char *prefix) +{ + if (!prefix || prefix[0] == '\0') return 1; + + for (; *line != '\0' && *prefix != '\0'; line++, prefix++) + { + if (*line != *prefix) + { + return 0; + } + } + + return *prefix == '\0'; +} + +static int strEndsWith(const char *line, const char *suffix) +{ + size_t a, b; + + if (!suffix || suffix[0] == '\0') return 1; + + a = strlen(line); + b = strlen(suffix); + return (a >= b) && (strcmp(line + (a - b), suffix) == 0); +} + +static const char *ctimespec(const struct timespec *ts, char *time_name, size_t len) +{ + time_t ltime = ts->tv_sec; + struct tm *currtime = localtime(<ime); + if (currtime == NULL) + { + return NULL; + } + + snprintf(time_name, len, "%04d%02d%02d_%02d:%02d:%02d", (currtime->tm_year + 1900), (currtime->tm_mon + 1), currtime->tm_mday, currtime->tm_hour, currtime->tm_min, + currtime->tm_sec); + return time_name; +} + +static int quectel_get_sysinfo_by_uevent(const char *uevent, MODULE_SYS_INFO *pSysInfo) +{ + FILE *fp; + char line[MAX_PATH]; + + if (!pSysInfo) + { + dbg_time("pSysInfo is NULL, errno: %d (%s)\n", errno, strerror(errno)); + return 0; + } + + memset(pSysInfo, 0x00, sizeof(MODULE_SYS_INFO)); + + fp = fopen(uevent, "r"); + if (fp == NULL) + { + dbg_time("fail to fopen %s, errno: %d (%s)\n", uevent, errno, strerror(errno)); + return 0; + } + + // dbg_time("%s\n", uevent); + while (fgets(line, sizeof(line), fp)) + { + if (line[strlen(line) - 1] == '\n' || line[strlen(line) - 1] == '\r') + { + line[strlen(line) - 1] = '\0'; + } + + // dbg_time("%s\n", line); + if (strStartsWith(line, "MAJOR=")) + { + pSysInfo->MAJOR = atoi(&line[strlen("MAJOR=")]); + } + else if (strStartsWith(line, "MINOR=")) + { + pSysInfo->MINOR = atoi(&line[strlen("MINOR=")]); + } + else if (strStartsWith(line, "DEVNAME=")) + { + strncpy(pSysInfo->DEVNAME, &line[strlen("DEVNAME=")], sizeof(pSysInfo->DEVNAME)); + } + else if (strStartsWith(line, "DEVTYPE=")) + { + strncpy(pSysInfo->DEVTYPE, &line[strlen("DEVTYPE=")], sizeof(pSysInfo->DEVTYPE)); + } + else if (strStartsWith(line, "PRODUCT=")) + { + strncpy(pSysInfo->PRODUCT, &line[strlen("PRODUCT=")], sizeof(pSysInfo->PRODUCT)); + } + } + + fclose(fp); + + return 1; +} + +// the return value is the number of quectel modules +int auto_find_quectel_modules(char *module_sys_path, unsigned size, const char *product, const struct timespec *atime) +{ + const char *base = "/sys/bus/usb/devices"; + DIR *busdir = NULL; + struct dirent *de = NULL; + int count = 0; + + busdir = opendir(base); + if (busdir == NULL) return -1; + + while ((de = readdir(busdir))) + { + static char uevent[MAX_PATH]; + static MODULE_SYS_INFO sysinfo; + + if (!isdigit(de->d_name[0])) continue; + + snprintf(uevent, sizeof(uevent), "%.24s/%.16s/uevent", base, de->d_name); + if (!quectel_get_sysinfo_by_uevent(uevent, &sysinfo)) continue; + + if (sysinfo.MAJOR != 189) continue; + + // dbg_time("MAJOR=%d, MINOR=%d, DEVNAME=%s, DEVTYPE=%s, PRODUCT=%s\n", + // sysinfo.MAJOR, sysinfo.MINOR, sysinfo.DEVNAME, sysinfo.DEVTYPE, sysinfo.PRODUCT); + + if (sysinfo.DEVTYPE[0] == '\0' || strStartsWith(sysinfo.DEVTYPE, "usb_device") == 0) continue; + + if (sysinfo.PRODUCT[0] == '\0') + { + continue; + } + + if (!(strStartsWith(sysinfo.PRODUCT, "2c7c/") // + || strStartsWith(sysinfo.PRODUCT, "5c6/9008") // + || strStartsWith(sysinfo.PRODUCT, "5c6/901f") // + || strStartsWith(sysinfo.PRODUCT, "5c6/9091") // + || strStartsWith(sysinfo.PRODUCT, "5c6/90db") // + || strStartsWith(sysinfo.PRODUCT, "3c93/ffff") // + || strStartsWith(sysinfo.PRODUCT, "3763/3c93"))) + { + continue; + } + + if (strStartsWith(sysinfo.PRODUCT, "5c6/90db")) + { + g_is_module_adb_entry_edl += 1; + } + + if ((strStartsWith(sysinfo.PRODUCT, "2c7c/6") || strStartsWith(sysinfo.PRODUCT, "2c7c/8")) && (sysinfo.PRODUCT[strlen("2c7c/6000")] == '/')) // skip ASR&HISI modules + { + if ((strStartsWith(sysinfo.PRODUCT, "2c7c/6008")) || strStartsWith(sysinfo.PRODUCT, "2c7c/6009")) + { + // EM061KGL, not ASR module, do not skip + } + else + continue; + } + + if (product && !strStartsWith(sysinfo.PRODUCT, product)) + { + dbg_time("skip %.24s/%s for PRODUCT %s is not %s\n", base, de->d_name, sysinfo.PRODUCT, product); + continue; + } + + if (atime) + { + struct timespec this_atime; + int speed; + + snprintf(uevent, sizeof(uevent), "%.24s/%.16s", base, de->d_name); + if (qusb_read_speed_atime(uevent, &this_atime, &speed)) + { + if ((this_atime.tv_sec < atime->tv_sec) || (this_atime.tv_sec == atime->tv_sec && this_atime.tv_nsec < atime->tv_nsec)) + { + char t1[64], t2[64]; + + dbg_time("skip %.24s/%s for atime {%s} old than {%s}\n", base, de->d_name, ctimespec(&this_atime, t1, sizeof(t1)), ctimespec(atime, t2, sizeof(t2))); + continue; + } + } + } + + snprintf(module_sys_path, size, "%.24s/%s", base, de->d_name); + count++; + dbg_time("[%d] %s %s\n", count, module_sys_path, sysinfo.PRODUCT); + } + + closedir(busdir); + return count; +} + +void quectel_get_ttyport_by_syspath(const char *module_sys_path, char *module_port_name, unsigned size) +{ + char infname[256]; + DIR *infdir = NULL; + struct dirent *de = NULL; + + module_port_name[0] = '\0'; + + sprintf(infname, "%s:1.%d", module_sys_path, usb_dm_interface); + infdir = opendir(infname); + if (infdir == NULL) return; + + while ((de = readdir(infdir))) + { + if (strStartsWith(de->d_name, "ttyUSB")) + { + snprintf(module_port_name, size, "/dev/%s", de->d_name); + break; + } + else if (!strncmp(de->d_name, "tty", strlen("tty"))) + { + sprintf(infname, "%s:1.%d/tty", module_sys_path, usb_dm_interface); + closedir(infdir); + infdir = opendir(infname); + if (infdir == NULL) break; + } + } + + if (infdir) closedir(infdir); +} + +static void quectel_fixup_sysport(const char *module_port_name, char *sysport, unsigned size) +{ + char syspath[MAX_PATH + 16]; + const char *sys_base = "/sys/class/tty"; + DIR *sys_dir = NULL; + struct dirent *dev = NULL; + + sysport[0] = '\0'; + sys_dir = opendir(sys_base); + if (!sys_dir) + { + dbg_time("fail to opendir('%s'), errno: %d (%s)\n", sys_base, errno, strerror(errno)); + return; + } + + while (NULL != (dev = readdir(sys_dir))) + { + if (!strncasecmp("ttyUSB", dev->d_name, strlen("ttyUSB"))) + { + MODULE_SYS_INFO sysinfo; + + snprintf(syspath, sizeof(syspath), "%.24s/%.16s/uevent", sys_base, dev->d_name); + if (quectel_get_sysinfo_by_uevent(syspath, &sysinfo)) + { + struct stat buf; + dev_t devt; + + devt = makedev(sysinfo.MAJOR, sysinfo.MINOR); + if (!stat(module_port_name, &buf) && buf.st_rdev == devt) + { + snprintf(sysport, size, "/sys/class/tty/%.16s", dev->d_name); + break; + } + } + } + } + closedir(sys_dir); +} + +void quectel_get_syspath_name_by_ttyport(const char *module_port_name, char *module_sys_path, unsigned size) +{ + char syspath[MAX_PATH]; + char sysport[64]; + int count; + char *pchar = NULL; + char dm_tty[24]; + + snprintf(dm_tty, sizeof(dm_tty), ":1.%d/tty", usb_dm_interface); + module_sys_path[0] = '\0'; + + snprintf(sysport, sizeof(sysport), "/sys/class/tty/%.48s", &module_port_name[strlen("/dev/")]); + if (access(sysport, F_OK) && errno == ENOENT) + { + quectel_fixup_sysport(module_port_name, sysport, sizeof(sysport)); // query real name + } + if (access(sysport, F_OK) && errno == ENOENT) return; + count = readlink(sysport, syspath, sizeof(syspath) - 1); + if (count < (int)strlen(dm_tty)) return; + + // ttyUSB0 -> + // ../../devices/soc0/soc/2100000.aips-bus/2184200.usb/ci_hdrc.1/usb1/1-1/1-1:1.0/ttyUSB0/tty/ttyUSB0 + pchar = strstr(syspath, dm_tty); + if (pchar == NULL) return; + + *pchar = '\0'; + while (*pchar != '/') pchar--; + + snprintf(module_sys_path, size, "/sys/bus/usb/devices/%.232s", pchar + 1); +} + +static void quectel_get_usb_device_info(const char *module_sys_path, struct quectel_usb_device *udev) +{ + static unsigned char devdesc[1024]; + size_t desclength, len; + char devname[64]; + int desc_fd; + __u8 bInterfaceNumber = 0; + int dev_mknod_and_delete_after_use = 0; + + MODULE_SYS_INFO sysinfo; + snprintf(devname, sizeof(devname), "%.56s/%s", module_sys_path, "uevent"); + if (!quectel_get_sysinfo_by_uevent(devname, &sysinfo)) return; + + snprintf(devname, sizeof(devname), "/dev/%.56s", sysinfo.DEVNAME); + if (access(devname, R_OK) && errno_nodev()) + { + // maybe Linux have create /sys/ device, but not ready to create /dev/ device. + usleep(100 * 1000); + } + + if (access(devname, R_OK) && errno_nodev()) + { + char *p = strstr(devname + strlen("/dev/"), "/"); + + while (p) + { + p[0] = '_'; + p = strstr(p, "/"); + } + + if (mknod(devname, S_IFCHR | 0666, MKDEV(sysinfo.MAJOR, sysinfo.MINOR))) + { + devname[1] = 't'; + devname[2] = 'm'; + devname[3] = 'p'; + + if (mknod(devname, S_IFCHR | 0666, MKDEV(sysinfo.MAJOR, sysinfo.MINOR))) + { + dbg_time("Fail to mknod %s, errno : %d (%s)\n", devname, errno, strerror(errno)); + return; + } + } + + dev_mknod_and_delete_after_use = 1; + } + + desc_fd = open(devname, O_RDWR | O_NOCTTY); + + if (dev_mknod_and_delete_after_use) + { + remove(devname); + } + + if (desc_fd <= 0) + { + dbg_time("fail to open %s, errno: %d (%s)\n", devname, errno, strerror(errno)); + return; + } + + desclength = read(desc_fd, devdesc, sizeof(devdesc)); + len = 0; + while (len < desclength) + { + struct usb_descriptor_header *h = (struct usb_descriptor_header *)(&devdesc[len]); + + if (h->bLength == sizeof(struct usb_device_descriptor) && h->bDescriptorType == USB_DT_DEVICE) + { + struct usb_device_descriptor *device = (struct usb_device_descriptor *)h; + + udev->idVendor = device->idVendor; + udev->idProduct = device->idProduct; + dbg_time("P: %s idVendor=%04x idProduct=%04x\n", devname, device->idVendor, device->idProduct); + } + else if (h->bLength == sizeof(struct usb_config_descriptor) && h->bDescriptorType == USB_DT_CONFIG) + { + struct usb_config_descriptor *config = (struct usb_config_descriptor *)h; + + dbg_time("C: %s bNumInterfaces: %d\n", devname, config->bNumInterfaces); + udev->bNumInterfaces = config->bNumInterfaces; + } + else if (h->bLength == sizeof(struct usb_interface_descriptor) && h->bDescriptorType == USB_DT_INTERFACE) + { + struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)h; + + dbg_time("I: If#= %d Alt= %d #EPs= %d Cls=%02x Sub=%02x Prot=%02x\n", interface->bInterfaceNumber, interface->bAlternateSetting, interface->bNumEndpoints, + interface->bInterfaceClass, interface->bInterfaceSubClass, interface->bInterfaceProtocol); + bInterfaceNumber = interface->bInterfaceNumber; + } + else if (h->bLength == USB_DT_ENDPOINT_SIZE && h->bDescriptorType == USB_DT_ENDPOINT) + { + if (bInterfaceNumber < EC20_MAX_INF) + { + struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)h; + + dbg_time("E: Ad=%02x Atr=%02x MxPS= %d Ivl=%dms\n", endpoint->bEndpointAddress, endpoint->bmAttributes, endpoint->wMaxPacketSize, endpoint->bInterval); + + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) + { + if (endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + udev->bulk_ep_in[bInterfaceNumber] = endpoint->bEndpointAddress; + else + udev->bulk_ep_out[bInterfaceNumber] = endpoint->bEndpointAddress; + udev->wMaxPacketSize[bInterfaceNumber] = endpoint->wMaxPacketSize; + } + else if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) + { + udev->intr_ep[bInterfaceNumber] = endpoint->bEndpointAddress; + } + } + } + else + { + } + + len += h->bLength; + } + + if (len == desclength) + { + strcpy(udev->devname, devname); + udev->desc = desc_fd; + } + + usb_dm_interface = 0; + + if ((udev->idVendor == 0x2c7c && udev->idProduct == 0x0127) // EM05CEFC-LNV Laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x0310) // EM05-CN Laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x030a) // EM05-G Laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x0311) // EM05-G-SE10 Laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x0315) // EM05-G STD Laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x0309) // EM05E-EDU Laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x6008) // EM061KGL laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x0128) // Google EM060KGL laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x6009) // EM061KGL laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x0803) // RM520NGL thinkpad 5G module dedicated pid + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x012E) // EM120K-GL laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x012F) // EM120K-GL laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x0804) // Customized PID for Zebra project laptop + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x030d)) // EM05G-FCCL Laptop + { + usb_dm_interface = 3; + } + else if ((udev->idVendor == 0x2c7c && udev->idProduct == 0x0514) // EG060K-EA + || (udev->idVendor == 0x2c7c && udev->idProduct == 0x0133)) // RG650VEU-rndis + { + usb_dm_interface = 2; + } + else if (udev->idVendor == 0x3c93 && udev->idProduct == 0xffff) // EG060K-EA + { + usb_dm_interface = 8; + } + else if (udev->idVendor == 0x05c6 && udev->idProduct == 0x90db) // (udev->idVendor == 0x05c6 && udev->idProduct == 0x90db) // AG600K-EM + { + usb_dm_interface = 2; + } + else if (udev->idVendor == 0x2c7c && udev->idProduct == 0x030b) // EG120KEABA-RNDIS + { + usb_dm_interface = 0; + if (g_from_ecm_to_rndis) usb_dm_interface = 2; + } +} + +static int usbfs_bulk_write(struct quectel_usb_device *udev, const void *data, int len, int timeout_msec, int need_zlp) +{ + struct usbdevfs_urb bulk; + struct usbdevfs_urb *urb = &bulk; + int n = -1; + int bInterfaceNumber = usb_dm_interface; + + (void)timeout_msec; + // if (urb->type == 0) + { + memset(urb, 0, sizeof(struct usbdevfs_urb)); + urb->type = USBDEVFS_URB_TYPE_BULK; + urb->endpoint = udev->bulk_ep_out[bInterfaceNumber]; + } + + urb->status = -1; + urb->buffer = (void *)data; + urb->buffer_length = len; + urb->usercontext = urb; + + if (need_zlp && (len % udev->wMaxPacketSize[bInterfaceNumber]) == 0) + { + // dbg_time("USBDEVFS_URB_ZERO_PACKET\n"); +#ifndef USBDEVFS_URB_ZERO_PACKET +#define USBDEVFS_URB_ZERO_PACKET 0x40 +#endif + urb->flags = USBDEVFS_URB_ZERO_PACKET; + } + else + { + urb->flags = 0; + } + + do + { + n = ioctl(udev->desc, USBDEVFS_SUBMITURB, urb); + } while ((n < 0) && (errno == EINTR)); + + if (n != 0) + { + dbg_time("inf[%d] USBDEVFS_SUBMITURB %d/%d, errno = %d (%s)\n", bInterfaceNumber, n, urb->buffer_length, errno, strerror(errno)); + return -1; + } + + do + { + urb = NULL; + n = ioctl(udev->desc, USBDEVFS_REAPURB, &urb); + } while ((n < 0) && (errno == EINTR)); + + if (n != 0) + { + dbg_time("inf[%d] ep_out %d/%d, errno = %d (%s)\n", bInterfaceNumber, n, urb->buffer_length, errno, strerror(errno)); + } + + // dbg_time("[ urb @%p status = %d, actual = %d ]\n", urb, urb->status, urb->actual_length); + + if (urb && urb->status == 0 && urb->actual_length) return urb->actual_length; + + return -1; +} + +static int poll_wait(int poll_fd, short events, int timeout_msec) +{ + struct pollfd pollfds[] = {{poll_fd, events, 0}}; + int ret; + + do + { + ret = poll(pollfds, 1, timeout_msec); + } while (ret == -1 && errno == EINTR); + + if (ret == 1 && (pollfds[0].revents & (events))) + return 0; + else if (ret == 0) + { // timeout + dbg_time("poll_wait events=%s msec=%d timeout\n", (events & POLLIN) ? "POLLIN" : "POLLOUT", timeout_msec); + return ETIMEDOUT; + } + + return EIO; +} + +static int usbfs_bulk_read(struct quectel_usb_device *udev, void *pbuf, int len, int timeout) +{ + struct usbdevfs_bulktransfer bulk; + int n = -1; + int bInterfaceNumber = usb_dm_interface; + + if (len < 512) + { + dbg_time("%s len=%d is too short\n", __func__, len); + return 0; + } + + bulk.ep = udev->bulk_ep_in[bInterfaceNumber]; + bulk.len = (len > MAX_USBFS_BULK_IN_SIZE) ? MAX_USBFS_BULK_IN_SIZE : len; + bulk.data = (void *)pbuf; + bulk.timeout = timeout; + + n = ioctl(udev->desc, USBDEVFS_BULK, &bulk); + if (n <= 0) + { + if (errno == ETIMEDOUT) + { + dbg_time("inf[%d] ep_in %d/%d, errno = %d (%s), timeout=%d\n", bInterfaceNumber, n, bulk.len, errno, strerror(errno), timeout); + n = 0; + } + else + dbg_time("inf[%d] ep_in %d/%d, errno = %d (%s)\n", bInterfaceNumber, n, bulk.len, errno, strerror(errno)); + } + + return n; +} + +static int qtcp_connect(const char *port_name, int *idVendor, int *idProduct, int *interfaceNum) +{ + int fd = -1; + char *tcp_host = strdup(port_name); + if (tcp_host == NULL) return -1; + + char *tcp_port = strchr(tcp_host, ':'); + struct sockaddr_in sockaddr; + TLV_USB tlv_usb; + + dbg_time("%s port_name = %s\n", __func__, port_name); + + if (tcp_port == NULL) + { + if (tcp_host) + { + free(tcp_host); + tcp_host = NULL; + } + return -1; + } + + *tcp_port++ = '\0'; + if (atoi(tcp_port) < 1 || atoi(tcp_port) > 0xFFFF) + { + if (tcp_host) + { + free(tcp_host); + tcp_host = NULL; + } + return -1; + } + + fd = socket(AF_INET, SOCK_STREAM, 0); + + if (fd <= 0) + { + if (tcp_host) + { + free(tcp_host); + tcp_host = NULL; + } + dbg_time("Device could not be socket: Linux System Errno: %s\n", strerror(errno)); + return -1; + } + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = inet_addr(tcp_host); + sockaddr.sin_port = htons(atoi(tcp_port)); + + if (tcp_host) + { + free(tcp_host); + tcp_host = NULL; + } + if (connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) + { + close(fd); + fd = -1; + dbg_time("Device could not be connect: Linux System Errno: %s\n", strerror(errno)); + return -1; + } + + // block read, untill usb2tcp tell me the usb device information + memset(&tlv_usb, 0x00, sizeof(tlv_usb)); + if (read(fd, &tlv_usb, sizeof(tlv_usb)) == -1) + { + }; + *idVendor = tlv_usb.idVendor; + *idProduct = tlv_usb.idProduct; + *interfaceNum = tlv_usb.interfaceNum; + + dbg_time("idVendor=%04x, idProduct=%04x, interfaceNum=%d\n", *idVendor, *idProduct, *interfaceNum); + + return fd; +} + +static int qtcp_read(int fd, void *pbuf, int size, int timeout_msec) +{ + static TLV tlv = {Q_USB2TCP_VERSION, 0}; + int cur = 0; + int len; + + if (tlv.length == 0) + { + len = read(fd, &tlv, sizeof(tlv)); + if (len != sizeof(tlv)) + { + dbg_time("%s read=%d, errno: %d (%s)\n", __func__, len, errno, strerror(errno)); + return 0; + } + + if (tlv.tag != Q_USB2TCP_VERSION) + { + dbg_time("%s tlv->tag=0x%x\n", __func__, tlv.tag); + return 0; + } + } + + if (size > tlv.length) size = tlv.length; + tlv.length -= size; + + while (cur < size) + { + if (poll_wait(fd, POLLIN, timeout_msec)) break; + + len = read(fd, (uint8_t *)pbuf + cur, size - cur); + if (len > 0) + { + cur += len; + } + else + { + dbg_time("%s read=%d, errno: %d (%s)\n", __func__, len, errno, strerror(errno)); + break; + } + } + + if (cur != size) + { + dbg_time("%s cur=%d, size=%d\n", __func__, cur, size); + } + + return cur; +} + +static int qtcp_write(int fd, void *pbuf, int size, int timeout_msec) +{ + TLV tlv = {Q_USB2TCP_VERSION, size}; + int cur = 0; + int len; + + len = write(fd, &tlv, sizeof(tlv)); + if (len != sizeof(tlv)) + { + dbg_time("%s write=%d, errno: %d (%s)\n", __func__, len, errno, strerror(errno)); + return 0; + } + + while (cur < size) + { + if (poll_wait(fd, POLLOUT, timeout_msec)) break; + + len = write(fd, (uint8_t *)pbuf + cur, size - cur); + if (len > 0) + { + cur += len; + } + else + { + dbg_time("%s write=%d, errno: %d (%s)\n", __func__, len, errno, strerror(errno)); + break; + } + } + + if (cur != size) + { + dbg_time("%s cur=%d, size=%d\n", __func__, cur, size); + } + + return cur; +} + +struct usbfs_getdriver +{ + unsigned int interface; + char driver[255 + 1]; +}; + +struct usbfs_ioctl +{ + int ifno; /* interface 0..N ; negative numbers reserved */ + int ioctl_code; /* MUST encode size + direction of data so the + * macros in give correct values */ + void *data; /* param buffer (in, or out) */ +}; + +#define IOCTL_USBFS_DISCONNECT _IO('U', 22) +#define IOCTL_USBFS_CONNECT _IO('U', 23) + +int usbfs_is_kernel_driver_alive(int fd, int ifnum) +{ + struct usbfs_getdriver getdrv; + getdrv.interface = ifnum; + if (ioctl(fd, USBDEVFS_GETDRIVER, &getdrv) < 0) + { + if (errno != ENODATA) dbg_time("%s ioctl USBDEVFS_GETDRIVER failed, errno: %d (%s)\n", __func__, errno, strerror(errno)); + return 0; + } + dbg_time("%s find interface %d has match the driver %s\n", __func__, ifnum, getdrv.driver); + return 1; +} + +void usbfs_detach_kernel_driver(int fd, int ifnum) +{ + struct usbfs_ioctl operate; + operate.data = NULL; + operate.ifno = ifnum; + operate.ioctl_code = IOCTL_USBFS_DISCONNECT; + if (ioctl(fd, USBDEVFS_IOCTL, &operate) < 0) + { + dbg_time("%s detach kernel driver failed\n", __func__); + } + else + { + dbg_time("%s detach kernel driver success\n", __func__); + } +} + +#define KVERSION(j, n, p) ((j)*1000000 + (n)*1000 + (p)) +static struct utsname utsname; /* for the kernel version */ +static int ql_get_kernel_version(void) +{ + int osmaj, osmin, ospatch; + int kernel_version; + + uname(&utsname); + osmaj = osmin = ospatch = 0; + sscanf(utsname.release, "%d.%d.%d", &osmaj, &osmin, &ospatch); + kernel_version = KVERSION(osmaj, osmin, ospatch); + + return kernel_version; +} + +static int detect_xhci_usb_zero_packet_bug_not_fix(const char *module_sys_path) +{ + char buf[256]; + int tmp; + char *driver; + + tmp = snprintf(buf, sizeof(buf), "/sys/bus/usb/devices/usb%c/../driver", module_sys_path[strlen("/sys/bus/usb/devices/")]); + driver = buf + (++tmp); + *driver = '\0'; + + tmp = readlink(buf, driver, sizeof(buf) - tmp); + if (tmp <= 0) return 0; + driver[tmp] = '\0'; + dbg_time("tmp=%s, driver=%s\n", buf, driver); + + if (!strstr(driver, "xhci")) return 0; + + tmp = ql_get_kernel_version(); + if (tmp >= KVERSION(4, 3, 0)) return 0; + + dbg_time("WARNNING ON File:%s Function:%s Line:%d\n", __FILE__, __func__, __LINE__); + dbg_time("The module attach to XHCI controller, but your kernel verison less than V4.3.0\n"); + dbg_time("Please make sure your kernel had apply patch 'usb: xhci: Add support for " + "URB_ZERO_PACKET to bulk/sg transfers'\n"); + dbg_time("https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/drivers/" + "usb/host/xhci-ring.c?id=4758dcd19a7d9ba9610b38fecb93f65f56f86346\n"); + sleep(2); // sleep 2 seconds, make sure FAE/customers can notice this warnning. + + return 1; +} + +void *qusb_noblock_open(const char *module_sys_path, int *idVendor, int *idProduct, int *interfaceNum) +{ + struct termios ios; + int retval; + int fd = -1; + struct quectel_usb_device *udev = &quectel_9x07; + + *idVendor = *idProduct = *interfaceNum = 0; + tcp_socket_fd = -1; + + if (module_sys_path && module_sys_path[0] == '/') + { + char port_name[64]; + + memset(udev, 0, sizeof(struct quectel_usb_device)); + quectel_get_usb_device_info(module_sys_path, udev); + if (udev->desc <= 0) return NULL; + quectel_get_ttyport_by_syspath(module_sys_path, port_name, sizeof(port_name)); + detect_xhci_usb_zero_packet_bug_not_fix(module_sys_path); + + *idVendor = udev->idVendor; + *idProduct = udev->idProduct; + *interfaceNum = udev->bNumInterfaces; + + if (port_name[0] == '\0' || (port_name[0] != '\0' && access(port_name, R_OK)) || (udev->idVendor == 0x05c6 && udev->idProduct == 0x9008)) + { + int bInterfaceNumber = usb_dm_interface; + + if (usbfs_is_kernel_driver_alive(udev->desc, bInterfaceNumber)) + { + usbfs_detach_kernel_driver(udev->desc, bInterfaceNumber); + } + retval = ioctl(udev->desc, USBDEVFS_CLAIMINTERFACE, &bInterfaceNumber); + if (retval != 0) + { + dbg_time("Fail to claim interface %d, errno: %d (%s)\n", bInterfaceNumber, errno, strerror(errno)); + if (udev->idVendor == 0x05c6) + { + int n; + struct + { + char infname[255 * 2]; + char driver[255 * 2]; + } *pl; + const char *driver = NULL; + + pl = (typeof(pl))malloc(sizeof(*pl)); + if (pl == NULL) + { + dbg_time("pl is NULL\n"); + return NULL; + } + + snprintf(pl->infname, sizeof(pl->infname), "%.255s:1.%d/driver", module_sys_path, usb_dm_interface); + n = readlink(pl->infname, pl->driver, sizeof(pl->driver)); + if (n > 0 && n < 510) + { + pl->driver[n] = '\0'; + while (pl->driver[n] != '/') n--; + driver = (&pl->driver[n + 1]); + } + + dbg_time("Error: when module in 'Emergency download mode', should not register " + "any usb driver\n"); + if (driver) + dbg_time("Error: it register to usb driver ' %s ' now, should delete " + "05c6&9008 from the source file of this driver\n", + driver); + if (driver && !strcmp(driver, "qcserial")) + dbg_time("Delete 05c6&9008 from 'drivers/usb/serial/qcserial.c' or disable " + "qcserial from kernel config\n"); + qusb_noblock_close(udev); + free(pl); + } + return NULL; + } + + udev->ttyfd = -1; + return udev; + } + else if (!access(port_name, R_OK)) + { + dbg_time("%s port_name = %s\n", __func__, port_name); + + fd = open(port_name, O_RDWR | O_SYNC); + + if (fd <= 0) + { + dbg_time("Device %s could not be open: Linux System Errno: %s", port_name, strerror(errno)); + return NULL; + } + + retval = tcgetattr(fd, &ios); + if (-1 == retval) + { + dbg_time("ermio settings could not be fetched Linux System Error:%s", strerror(errno)); + if (fd > 0) + { + close(fd); + fd = -1; + } + return NULL; + } + + cfmakeraw(&ios); + cfsetispeed(&ios, B115200); + cfsetospeed(&ios, B115200); + + retval = tcsetattr(fd, TCSANOW, &ios); + if (-1 == retval) + { + dbg_time("Device could not be configured: Linux System Errno: %s", strerror(errno)); + } + udev->ttyfd = fd; + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); + + return udev; + } + else + { + dbg_time("fail to access %s errno: %d (%s)\n", port_name, errno, strerror(errno)); + } + } + else if (module_sys_path && module_sys_path[0] != '/') + { + fd = qtcp_connect(module_sys_path, idVendor, idProduct, interfaceNum); + if (fd > 0) + { + tcp_socket_fd = fd; + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); + return &tcp_socket_fd; + } + } + + return NULL; +} + +int qusb_noblock_close(void *handle) +{ + struct quectel_usb_device *udev = &quectel_9x07; + + if (handle == &tcp_socket_fd) + { + close(tcp_socket_fd); + tcp_socket_fd = -1; + } + if (handle == udev && udev->ttyfd > 0) + { + close(udev->ttyfd); + udev->ttyfd = -1; + close(udev->desc); + udev->desc = -1; + } + else if (handle == udev && udev->desc > 0) + { + int bInterfaceNumber = usb_dm_interface; + ioctl(udev->desc, USBDEVFS_RELEASEINTERFACE, &bInterfaceNumber); + close(udev->desc); + udev->desc = -1; + } + else if (handle == &edl_pcie_mhifd && edl_pcie_mhifd > 0) + { + close(edl_pcie_mhifd); + edl_pcie_mhifd = -1; + } + memset(udev, 0, sizeof(*udev)); + + return 0; +} + +int qusb_use_usbfs_interface(const void *handle) +{ + struct quectel_usb_device *udev = &quectel_9x07; + + return (handle == udev && udev->ttyfd <= 0 && udev->desc > 0); +} + +int qusb_read_speed_atime(const char *module_sys_path, struct timespec *out_time, int *out_speed) +{ + char speed[256]; + int fd; + struct stat stat; + + snprintf(speed, sizeof(speed), "%.240s/speed", module_sys_path); + fd = open(speed, O_RDONLY); + if (fd == -1) return 0; + + if (read(fd, speed, sizeof(speed))) + { + } + fstat(fd, &stat); + close(fd); + fd = -1; + + *out_speed = atoi(speed); + +#ifdef ANDROID + struct timespec out_time_tmp; + memset(&out_time_tmp, 0, sizeof(struct timespec)); + out_time_tmp.tv_sec = stat.st_atime; + *out_time = out_time_tmp; +#else + *out_time = stat.st_atim; +#endif + + dbg_time("%s speed: %d, st_atime: %s\n", __func__, *out_speed, ctimespec(out_time, speed, sizeof(speed))); + return 1; +} + +int qusb_noblock_read(const void *handle, void *pbuf, int max_size, int min_size, int timeout_msec) +{ + struct quectel_usb_device *udev = &quectel_9x07; + int cur = 0; + int poll_ret = 0; + + if (min_size == 0) min_size = 1; + if (timeout_msec == 0) timeout_msec = 3000; + +#if 0 // depend on your worst net speed + if (handle == &tcp_socket_fd) { + if (timeout_msec > 1000) //before sahala&firebose, we allow read timeout occurs + timeout_msec = 120*1000; + } +#endif + + while (cur < min_size) + { + int len = 0; + + if (handle == &tcp_socket_fd) + { + if ((poll_ret = poll_wait(tcp_socket_fd, POLLIN, timeout_msec))) break; + len = qtcp_read(tcp_socket_fd, (uint8_t *)pbuf + cur, max_size - cur, timeout_msec); + } + else if (handle == udev && udev->ttyfd > 0) + { + if ((poll_ret = poll_wait(udev->ttyfd, POLLIN, timeout_msec))) break; + len = read(udev->ttyfd, (uint8_t *)pbuf + cur, max_size - cur); + } + else if (handle == udev && udev->desc > 0) + { + len = usbfs_bulk_read(udev, (uint8_t *)pbuf + cur, max_size - cur, timeout_msec); + } + else if (handle == &edl_pcie_mhifd && edl_pcie_mhifd > 0) + { + if ((poll_ret = poll_wait(edl_pcie_mhifd, POLLIN, timeout_msec))) break; + len = read(edl_pcie_mhifd, (uint8_t *)pbuf + cur, max_size - cur); + } + else + { + break; + } + + if (len > 0) + { + cur += len; + } + else + { + dbg_time("%s read=%d, errno: %d (%s)\n", __func__, len, errno, strerror(errno)); + break; + } + } + + if (poll_ret == EIO) + return -1; + else if (poll_ret == ETIMEDOUT) + return cur; + + if (cur < min_size) + { + dbg_time("%s cur=%d, min_size=%d\n", __func__, cur, min_size); + } + + return cur; +} + +int qusb_noblock_write(const void *handle, void *pbuf, int max_size, int min_size, int timeout_msec, int need_zlp) +{ + struct quectel_usb_device *udev = &quectel_9x07; + int cur = 0; + + if (min_size == 0) min_size = 1; + if (timeout_msec == 0) timeout_msec = 3000; + +#if 0 // depend on your worst net speed + if (handle == &tcp_socket_fd) { + timeout_msec = 120*1000; + } +#endif + + while (cur < min_size) + { + int len = 0; + + if (handle == &tcp_socket_fd) + { + if (poll_wait(tcp_socket_fd, POLLOUT, timeout_msec)) break; + len = qtcp_write(tcp_socket_fd, (uint8_t *)pbuf + cur, max_size - cur, timeout_msec); + } + else if (handle == udev && udev->ttyfd > 0) + { + if (poll_wait(udev->ttyfd, POLLOUT, timeout_msec)) break; + len = write(udev->ttyfd, (uint8_t *)pbuf + cur, max_size - cur); + } + else if (handle == udev && udev->desc > 0) + { + len = usbfs_bulk_write(udev, (uint8_t *)pbuf + cur, max_size - cur, timeout_msec, need_zlp); + } + else if (handle == &edl_pcie_mhifd && edl_pcie_mhifd > 0) + { + if (poll_wait(edl_pcie_mhifd, POLLOUT, timeout_msec)) break; + len = write(edl_pcie_mhifd, (uint8_t *)pbuf + cur, max_size - cur); + } + else + { + break; + } + + if (len > 0) + { + cur += len; + } + else + { + dbg_time("%s write=%d, errno: %d (%s)\n", __func__, len, errno, strerror(errno)); + break; + } + } + + if (cur < min_size) + { + dbg_time("%s cur=%d, min_size=%d\n", __func__, cur, min_size); + } + + return cur; +} + +int qfile_find_file(const char *dir, const char *prefix, const char *suffix, char **filename) +{ + DIR *pdir; + struct dirent *ent = NULL; + pdir = opendir(dir); + if (pdir == NULL) + { + return 0; + } + + *filename = NULL; + if (pdir) + { + while ((ent = readdir(pdir)) != NULL) + { + if (strStartsWith(ent->d_name, prefix) && strEndsWith(ent->d_name, suffix)) + { + dbg_time("find '%s'\n", ent->d_name); + *filename = strdup(ent->d_name); + break; + } + } + } + + closedir(pdir); + return *filename != NULL; +} + +const char *firehose_get_time(void) +{ + static char time_buf[50]; + struct timeval tv; + static int s_start_msec = -1; + int now_msec, cost_msec; + + gettimeofday(&tv, NULL); + now_msec = tv.tv_sec * 1000; + now_msec += (tv.tv_usec + 500) / 1000; + + if (s_start_msec == -1) + { + s_start_msec = now_msec; + } + + cost_msec = now_msec - s_start_msec; + + sprintf(time_buf, "[%03d.%03d]", cost_msec / 1000, cost_msec % 1000); + return time_buf; +} + +// void dbg_time (const char *fmt, ...) { +// va_list args; +// va_start(args, fmt); +// static char line[2048]; +// snprintf(line, sizeof(line), "%s ", firehose_get_time()); +// vsnprintf(line + strlen(line), sizeof(line) - strlen(line), fmt, args); +// fprintf(stdout, "%s", line); +// fflush(stdout); +// } + +int qpcie_open(const char *firehose_dir, const char *firehose_mbn, const char *module_port_name) +{ + int bhifd, edlfd, diagfd; + long ret; + FILE *fp; + char prog_firehose_sdx24[256 + 32]; + size_t filesize; + void *filebuf; + int cur_number = 0; + + BHI_INFO_TYPE *bhi_info = malloc(sizeof(BHI_INFO_TYPE)); + if (bhi_info == NULL) + { + dbg_time("bhi_info is NULL, errno: %d (%s)\n", errno, strerror(errno)); + error_return(); + } + + if (is_upgrade_fimeware_zip_7z) + { + snprintf(prog_firehose_sdx24, sizeof(prog_firehose_sdx24), "/tmp/%.240s", firehose_mbn); + dbg_time("%s prog_firehose_sdx24:%s\n", __func__, prog_firehose_sdx24); + } + else + { + snprintf(prog_firehose_sdx24, sizeof(prog_firehose_sdx24), "%.240s/%.32s", firehose_dir, firehose_mbn); + } + + fp = fopen(prog_firehose_sdx24, "rb"); + if (fp == NULL) + { + dbg_time("fail to fopen %s, errno: %d (%s)\n", prog_firehose_sdx24, errno, strerror(errno)); + if (bhi_info) + { + free(bhi_info); + bhi_info = NULL; + } + error_return(); + } + + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + filebuf = malloc(sizeof(filesize) + filesize); + if (filebuf == NULL) + { + dbg_time("filebuf is NULL, errno: %d (%s)\n", errno, strerror(errno)); + if (bhi_info) + { + free(bhi_info); + bhi_info = NULL; + } + + if (fp) + { + fclose(fp); + fp = NULL; + } + error_return(); + } + memcpy(filebuf, &filesize, sizeof(filesize)); + if (fread((uint8_t *)filebuf + sizeof(filesize), 1, filesize, fp) == (size_t)0) + { + }; + fclose(fp); + + if (is_upgrade_fimeware_zip_7z) + { + unlink(prog_firehose_sdx24); + } + + int i; + for (i = 0; i < pcie_port_defult.pcie_port_number; i++) + { + if (!strncasecmp(module_port_name, pcie_port_defult.pcie_port_one[i].bhi, 28)) + { + cur_number = i; + break; + } + } + + if (i == pcie_port_defult.pcie_port_number) + { + dbg_time("PCIE port assignment error\n"); + if (bhi_info) + { + free(bhi_info); + bhi_info = NULL; + } + + if (filebuf) + { + free(filebuf); + filebuf = NULL; + } + error_return(); + } + + diagfd = open(pcie_port_defult.pcie_port_one[cur_number].diag, O_RDWR | O_NOCTTY); + if (diagfd > 0) + { + int edl_retry = 30; // SDX55 require long time by now 20190412 + void *usb_handle = &edl_pcie_mhifd; + edl_pcie_mhifd = diagfd; + + while (access(pcie_port_defult.pcie_port_one[cur_number].diag, R_OK) == 0 && edl_retry-- > 0) + { + dbg_time("switch_to_edl_mode\n"); + switch_to_edl_mode(usb_handle); + sleep(1); + } + + close(diagfd); + diagfd = -1; + edl_pcie_mhifd = -1; + } + + sleep(1); // see https://ticket.quectel.com/browse/FAE-39737 + bhifd = open(pcie_port_defult.pcie_port_one[cur_number].bhi, O_RDWR | O_NOCTTY); + if (bhifd <= 0) + { + dbg_time("fail to open %s, errno: %d (%s)\n", "/dev/mhi_BHI", errno, strerror(errno)); + if (bhi_info) + { + free(bhi_info); + bhi_info = NULL; + } + + if (filebuf) + { + free(filebuf); + filebuf = NULL; + } + + if (diagfd > 0) + { + close(diagfd); + diagfd = -1; + } + error_return(); + } + + ret = ioctl(bhifd, IOCTL_BHI_GETDEVINFO, bhi_info); + if (ret) + { + dbg_time("fail to ioctl IOCTL_BHI_GETDEVINFO, errno: %d (%s)\n", errno, strerror(errno)); + if (bhi_info) + { + free(bhi_info); + bhi_info = NULL; + } + + if (filebuf) + { + free(filebuf); + filebuf = NULL; + } + + if (diagfd > 0) + { + close(diagfd); + diagfd = -1; + } + + if (bhifd > 0) + { + close(bhifd); + bhifd = -1; + } + error_return(); + } + + dbg_time("bhi_ee = %d\n", bhi_info->bhi_ee); + if (bhi_info->bhi_ee != MHI_EE_EDL) + { + dbg_time("bhi_ee is not MHI_EE_EDL\n"); + close(bhifd); + bhifd = -1; + free(filebuf); + filebuf = NULL; + if (bhi_info) + { + free(bhi_info); + bhi_info = NULL; + } + + if (diagfd > 0) + { + close(diagfd); + diagfd = -1; + } + error_return(); + } + + if (bhi_info) + { + free(bhi_info); + bhi_info = NULL; + } + + ret = ioctl(bhifd, IOCTL_BHI_WRITEIMAGE, filebuf); + if (ret) + { + dbg_time("fail to ioctl IOCTL_BHI_GETDEVINFO, errno: %d (%s)\n", errno, strerror(errno)); + if (filebuf) + { + free(filebuf); + filebuf = NULL; + } + + if (diagfd > 0) + { + close(diagfd); + diagfd = -1; + } + + if (bhifd > 0) + { + close(bhifd); + bhifd = -1; + } + error_return(); + } + + if (bhifd > 0) + { + close(bhifd); + bhifd = -1; + } + if (filebuf) + { + free(filebuf); + filebuf = NULL; + } + + sleep(1); + edlfd = open(pcie_port_defult.pcie_port_one[cur_number].edl, O_RDWR | O_NOCTTY); + if (edlfd <= 0) + { + dbg_time("fail to access %s, errno: %d (%s)\n", pcie_port_defult.pcie_port_one[cur_number].edl, errno, strerror(errno)); + if (diagfd > 0) + { + close(diagfd); + diagfd = -1; + } + error_return(); + } + + edl_pcie_mhifd = edlfd; + + if (diagfd > 0) + { + close(diagfd); + diagfd = -1; + } + + return 0; +} + +int usbmon_fd = -1; +int usbmon_logfile_fd = -1; + +void *catch_log(void *arg) +{ + int nreads = 0; + char tbuff[256]; + size_t off = strlen("[999.999] "); + + (void)arg; + tbuff[off - 1] = ' '; + while (1) + { + nreads = read(usbmon_fd, tbuff + off, sizeof(tbuff) - off - 1); + if (nreads == -1 && errno == EINTR) continue; + if (nreads <= 0) break; + + tbuff[off + nreads] = '\0'; + memcpy(tbuff, firehose_get_time(), off - 1); + + if (write(usbmon_logfile_fd, tbuff, strlen(tbuff)) == -1) + { + }; + } + + return NULL; +} + +int ql_capture_usbmon_log(const char *usbmon_logfile) +{ + const char *usbmon_path = "/sys/kernel/debug/usb/usbmon/0u"; + pthread_t pt; + pthread_attr_t attr; + + if (access("/sys/kernel/debug/usb", F_OK)) + { + dbg_time("debugfs is not mount, please execute \"mount -t debugfs none_debugs " + "/sys/kernel/debug\"\n"); + return -1; + } + if (access("/sys/kernel/debug/usb/usbmon", F_OK)) + { + dbg_time("usbmon is not load, please execute \"modprobe usbmon\" or \"insmod usbmon.ko\"\n"); + return -1; + } + + usbmon_fd = open(usbmon_path, O_RDONLY); + if (usbmon_fd < 0) + { + dbg_time("open %s error(%d) (%s)\n", usbmon_path, errno, strerror(errno)); + return -1; + } + + usbmon_logfile_fd = open(usbmon_logfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (usbmon_logfile_fd < 0) + { + dbg_time("open %s error(%d) (%s)\n", usbmon_logfile, errno, strerror(errno)); + close(usbmon_fd); + usbmon_fd = -1; + return -1; + } + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&pt, &attr, catch_log, NULL); + + // pthread_attr_destroy(&attr); //aaron 2023.07.27 + return 0; +} + +void ql_stop_usbmon_log() +{ + if (usbmon_logfile_fd > 0) + { + close(usbmon_logfile_fd); + usbmon_logfile_fd = -1; + } + if (usbmon_fd > 0) + { + close(usbmon_fd); + usbmon_fd = -1; + } +} diff --git a/application/qfirehose/src/usb_linux.h b/application/qfirehose/src/usb_linux.h new file mode 100644 index 0000000..9a6ad84 --- /dev/null +++ b/application/qfirehose/src/usb_linux.h @@ -0,0 +1,193 @@ +/* + Copyright 2023 Quectel Wireless Solutions Co.,Ltd + + Quectel hereby grants customers of Quectel a license to use, modify, + distribute and publish the Software in binary form provided that + customers shall have no right to reverse engineer, reverse assemble, + decompile or reduce to source code form any portion of the Software. + Under no circumstances may customers modify, demonstrate, use, deliver + or disclose any portion of the Software in source code form. +*/ + +#ifndef __QFIREHOSE_USB_LINUX_H__ +#define __QFIREHOSE_USB_LINUX_H__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_PATH 256 +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define ZIP_INFO "/tmp/zip_info.txt" +#define ZIP_PROCESS_INFO "/tmp/zip_process_info.txt" + +#define safe_free(p) \ + do \ + { \ + if (p != NULL) \ + { \ + free((void *)p); \ + p = NULL; \ + } \ + } while (0) + +extern char zip_cmd_buf[512]; +extern int g_is_module_adb_entry_edl; + +typedef struct module_sys_info +{ + /* + MAJOR=189 + MINOR=1 + DEVNAME=bus/usb/001/002 + DEVTYPE=usb_device + DRIVER=usb + PRODUCT=2c7c/415/318 + TYPE=239/2/1 + BUSNUM=001 + */ + // char sys_path[MAX_PATH]; + int MAJOR; + int MINOR; + char DEVNAME[64]; + char DEVTYPE[64]; + char PRODUCT[64]; +} MODULE_SYS_INFO; + +void *qusb_noblock_open(const char *module_sys_path, int *idVendor, int *idProduct, int *interfaceNum); +int qusb_noblock_close(void *handle); +int qusb_noblock_write(const void *handle, void *pbuf, int max_size, int min_size, int timeout_msec, int need_zlp); +int qusb_noblock_read(const void *handle, void *pbuf, int max_size, int min_size, int timeout_msec); +int qusb_read_speed_atime(const char *module_sys_path, struct timespec *out_time, int *out_speed); +int qfile_find_file(const char *dir, const char *prefix, const char *suffix, char **filename); +#define errno_nodev() (errno == ENOENT || errno == ENODEV) +// void dbg_time (const char *fmt, ...); +const char *firehose_get_time(void); +extern FILE *loghandler; +#ifdef FH_DEBUG +#define dbg_time(fmt, args...) \ + do \ + { \ + fprintf(stdout, "[%15s-%04d]%s: " fmt, __FILE__, __LINE__, firehose_get_time(), ##args); \ + fflush(stdout); \ + if (loghandler) fprintf(loghandler, "[%15s-%04d]%s: " fmt, __FILE__, __LINE__, firehose_get_time(), ##args); \ + } while (0); +#else +#define dbg_time(fmt, args...) \ + do \ + { \ + fprintf(stdout, "%s: " fmt, firehose_get_time(), ##args); \ + fflush(stdout); \ + if (loghandler) fprintf(loghandler, "%s: " fmt, firehose_get_time(), ##args); \ + } while (0); +#endif +double get_now(); +void get_duration(double start); +int update_transfer_bytes(long long bytes_cur); +void set_transfer_allbytes(long long bytes); +int auto_find_quectel_modules(char *module_sys_path, unsigned size, const char *product, const struct timespec *atime); +void quectel_get_syspath_name_by_ttyport(const char *module_port_name, char *module_sys_path, unsigned size); +void quectel_get_ttyport_by_syspath(const char *module_sys_path, char *module_port_name, unsigned size); +#define error_return() \ + do \ + { \ + dbg_time("%s %s %d fail\n", __FILE__, __func__, __LINE__); \ + return __LINE__; \ + } while (0) + +extern int edl_pcie_mhifd; + +#define IOCTL_BHI_GETDEVINFO 0x8BE0 + 1 +#define IOCTL_BHI_WRITEIMAGE 0x8BE0 + 2 + +typedef unsigned int ULONG; + +typedef struct _bhi_info_type +{ + ULONG bhi_ver_minor; + ULONG bhi_ver_major; + ULONG bhi_image_address_low; + ULONG bhi_image_address_high; + ULONG bhi_image_size; + ULONG bhi_rsvd1; + ULONG bhi_imgtxdb; + ULONG bhi_rsvd2; + ULONG bhi_msivec; + ULONG bhi_rsvd3; + ULONG bhi_ee; + ULONG bhi_status; + ULONG bhi_errorcode; + ULONG bhi_errdbg1; + ULONG bhi_errdbg2; + ULONG bhi_errdbg3; + ULONG bhi_sernum; + ULONG bhi_sblantirollbackver; + ULONG bhi_numsegs; + ULONG bhi_msmhwid[6]; + ULONG bhi_oempkhash[48]; + ULONG bhi_rsvd5; +} BHI_INFO_TYPE, *PBHI_INFO_TYPE; + +enum MHI_EE +{ + MHI_EE_PBL = 0x0, /* Primary Boot Loader */ + MHI_EE_SBL = 0x1, /* Secondary Boot Loader */ + MHI_EE_AMSS = 0x2, /* AMSS Firmware */ + MHI_EE_RDDM = 0x3, /* WIFI Ram Dump Debug Module */ + MHI_EE_WFW = 0x4, /* WIFI (WLAN) Firmware */ + MHI_EE_PT = 0x5, /* PassThrough, Non PCIe BOOT (PCIe is BIOS locked, not used for boot */ + MHI_EE_EDL = 0x6, /* PCIe enabled in PBL for emergency download (Non PCIe BOOT) */ + MHI_EE_FP = 0x7, /* FlashProg, Flash Programmer Environment */ + MHI_EE_BHIE = MHI_EE_FP, + MHI_EE_UEFI = 0x8, /* UEFI */ + + MHI_EE_DISABLE_TRANSITION = 0x9, + MHI_EE_MAX +}; +int qpcie_open(const char *firehose_dir, const char *firehose_mbn, const char *module_port_name); + +#define Q_USB2TCP_VERSION 0x12345678 +typedef struct +{ + int tag; + int length; + int value[]; +} TLV; + +typedef struct +{ + int tag; + int length; + int idVendor; + int idProduct; + int interfaceNum; +} TLV_USB; + +typedef struct +{ + char zip_file_name_backup[128]; + char zip_file_dir_backup[256]; +} file_name_backup_count; + +typedef struct +{ + int file_name_count; + file_name_backup_count file_backup_c[256]; // smart SA885GAPNA number of image file is 96 +} file_name_backup; + +extern file_name_backup file_name_b; + +extern int is_upgrade_fimeware_zip_7z; +extern int is_firehose_zip_7z_name_exit; +extern int is_upgrade_fimeware_only_zip; +extern unsigned int g_from_ecm_to_rndis; +#endif diff --git a/luci/luci-app-qmodem/Makefile b/luci/luci-app-qmodem/Makefile index beb1d16..b561bd2 100644 --- a/luci/luci-app-qmodem/Makefile +++ b/luci/luci-app-qmodem/Makefile @@ -27,6 +27,7 @@ LUCI_DEPENDS:=+luci-compat \ +usbutils \ +PACKAGE_luci-app-qmodem_INCLUDE_ADD_PCI_SUPPORT:kmod-pcie_mhi \ +PACKAGE_luci-app-qmodem_INCLUDE_ADD_PCI_SUPPORT:pciutils \ + +PACKAGE_luci-app-qmodem_INCLUDE_ADD_QFIREHOSE_SUPPORT:qfirehose \ +PACKAGE_luci-app-qmodem_USE_TOM_CUSTOMIZED_QUECTEL_CM:quectel-CM-5G-M \ +PACKAGE_luci-app-qmodem_USING_QWRT_QUECTEL_CM_5G:quectel-CM-5G \ +PACKAGE_luci-app-qmodem_USING_NORMAL_QUECTEL_CM:quectel-cm \ @@ -69,6 +70,10 @@ endchoice config PACKAGE_luci-app-qmodem_INCLUDE_ADD_PCI_SUPPORT bool "Add PCIe Modem SUPPORT" default n + + config PACKAGE_luci-app-qmodem_INCLUDE_ADD_QFIREHOSE_SUPPORT + bool "Add Qfirehose SUPPORT" + default n endef define Package/luci-app-qmodem_with_lua_index_page