diff options
60 files changed, 6158 insertions, 1085 deletions
diff --git a/.checkpatch.conf b/.checkpatch.conf index 32625b5..8ea7ed1 100644 --- a/.checkpatch.conf +++ b/.checkpatch.conf @@ -14,3 +14,10 @@ --ignore PREFER_PRINTF --ignore PREFER_SCANF --ignore PREFER_SECTION + +# We currently have only one maintainer. It would be great to have more +# co-maintainers, however until then we don't need to check for file patch +# changes. Feel free to step in if you want to maintain the devices you added, +# especially if it's a device that is not supported by Replicant or that +# Replicant doesn't want to support for some reason. +--ignore FILE_PATH_CHANGES @@ -1,3 +1,42 @@ +# Tests +/samsung-ipc/tests/libsamsung-ipc-test +/samsung-ipc/tests/libsamsung-ipc-test.log +/samsung-ipc/tests/libsamsung-ipc-test.trs +/samsung-ipc/tests/test-suite.log +/test-driver +/tools/ipc-modem.log +/tools/ipc-modem/tests/ipc-modem.log +/tools/ipc-modem/tests/ipc-modem.trs +/tools/ipc-modem/test-suite.log +/tools/ipc-modem.trs +/tools/nv_data-imei.log +/tools/nv_data-imei.trs +/tools/nv_data-md5.log +/tools/nv_data-md5.trs +/tools/tests/nv_data-imei.log +/tools/tests/nv_data-imei.trs +/tools/tests/nv_data-md5.log +/tools/tests/nv_data-md5.trs +/tools/test-suite.log +valgrind.*.log + +# scripts/PKGBUILD +/scripts/libsamsung-ipc-git-*.pkg.tar.xz +/scripts/libsamsung-ipc-git-debug-*.pkg.tar.xz +/scripts/pkg/** +/scripts/src/** + +# Tools +/tools/https-send-sms +/tools/ipc-modem/ipc-modem +/tools/ipc-test +/tools/nv_data-imei +/tools/nv_data-md5 + +# Text editors +\#*\# +*~ + Makefile.in Makefile *.o @@ -29,6 +68,3 @@ INSTALL *.pc build core -ipc-modem -ipc-test -nv_data-md5 @@ -17,65 +17,73 @@ LOCAL_PATH := $(call my-dir) +ifneq (,$(filter galaxysmtd galaxytab,$(TARGET_DEVICE))) + ipc_device_name := aries +endif + ifneq (,$(filter crespo,$(TARGET_DEVICE))) ipc_device_name := crespo endif -ifneq (,$(filter galaxysmtd galaxytab,$(TARGET_DEVICE))) - ipc_device_name := aries +ifneq (,$(filter herolte,$(TARGET_DEVICE))) + ipc_device_name := herolte endif ifneq (,$(filter i9100 galaxys2 n7000,$(TARGET_DEVICE))) ipc_device_name := galaxys2 endif -ifneq (,$(filter maguro,$(TARGET_DEVICE))) - ipc_device_name := maguro +ifneq (,$(filter i9300,$(TARGET_DEVICE))) + ipc_device_name := i9300 endif -ifneq (,$(filter p5100 p3100 espresso3g,$(TARGET_DEVICE))) - ipc_device_name := piranha +ifneq (,$(filter maguro,$(TARGET_DEVICE))) + ipc_device_name := maguro endif -ifneq (,$(filter i9300,$(TARGET_DEVICE))) - ipc_device_name := i9300 +ifneq (,$(filter n5100,$(TARGET_DEVICE))) + ipc_device_name := n5100 endif ifneq (,$(filter n7100,$(TARGET_DEVICE))) ipc_device_name := n7100 endif -ifneq (,$(filter n5100,$(TARGET_DEVICE))) - ipc_device_name := n5100 +ifneq (,$(filter p5100 p3100 espresso3g,$(TARGET_DEVICE))) + ipc_device_name := piranha endif libsamsung_ipc_local_src_files := \ + samsung-ipc/call.c \ + samsung-ipc/gen.c \ + samsung-ipc/gprs.c \ samsung-ipc/ipc.c \ + samsung-ipc/ipc_strings.c \ samsung-ipc/ipc_utils.c \ - samsung-ipc/modems/xmm616/xmm616.c \ - samsung-ipc/modems/xmm626/xmm626.c \ - samsung-ipc/modems/xmm626/xmm626_hsic.c \ - samsung-ipc/modems/xmm626/xmm626_kernel_smdk4412.c \ - samsung-ipc/modems/xmm626/xmm626_mipi.c \ + samsung-ipc/misc.c \ + samsung-ipc/net.c \ + samsung-ipc/rfs.c \ + samsung-ipc/sec.c \ + samsung-ipc/sms.c \ + samsung-ipc/svc.c \ + samsung-ipc/utils.c \ samsung-ipc/devices/ipc_devices.c \ - samsung-ipc/devices/crespo/crespo.c \ samsung-ipc/devices/aries/aries.c \ + samsung-ipc/devices/crespo/crespo.c \ samsung-ipc/devices/galaxys2/galaxys2.c \ - samsung-ipc/devices/maguro/maguro.c \ - samsung-ipc/devices/piranha/piranha.c \ + samsung-ipc/devices/herolte/herolte.c \ samsung-ipc/devices/i9300/i9300.c \ - samsung-ipc/devices/n7100/n7100.c \ + samsung-ipc/devices/maguro/maguro.c \ samsung-ipc/devices/n5100/n5100.c \ - samsung-ipc/utils.c \ - samsung-ipc/call.c \ - samsung-ipc/sms.c \ - samsung-ipc/sec.c \ - samsung-ipc/net.c \ - samsung-ipc/misc.c \ - samsung-ipc/svc.c \ - samsung-ipc/gprs.c \ - samsung-ipc/rfs.c \ - samsung-ipc/gen.c + samsung-ipc/devices/n7100/n7100.c \ + samsung-ipc/devices/piranha/piranha.c \ + samsung-ipc/modems/xmm616/xmm616.c \ + samsung-ipc/modems/xmm626/xmm626.c \ + samsung-ipc/modems/xmm626/xmm626_hsic.c \ + samsung-ipc/modems/xmm626/xmm626_kernel_smdk4412.c \ + samsung-ipc/modems/xmm626/xmm626_mipi.c \ + samsung-ipc/partitions/android/android.c \ + samsung-ipc/partitions/toc/toc.c libsamsung_ipc_local_export_headers := \ include/call.h \ @@ -105,9 +113,11 @@ libsamsung_ipc_local_c_includes := \ $(LOCAL_PATH)/samsung-ipc \ external/openssl/include -libsamsung_local_cflags := \ - -DIPC_DEVICE_NAME=\"$(ipc_device_name)\" \ - -DDEBUG +libsamsung_local_cflags := -DDEBUG + +ifneq ($(TARGET_DEVICE),) +libsamsung_local_cflags += -DIPC_DEVICE_NAME=\"$(ipc_device_name)\" +endif # ifneq ($(TARGET_DEVICE),) libsamsung_ipc_local_shared_libraries := \ libutils \ @@ -117,6 +127,7 @@ libsamsung_ipc_local_shared_libraries := \ # Static library version of libsamsung-ipc # ############################################ include $(CLEAR_VARS) +include $(LOCAL_PATH)/android_versions.mk LOCAL_MODULE := libsamsung-ipc LOCAL_MODULE_TAGS := optional @@ -135,6 +146,7 @@ include $(BUILD_STATIC_LIBRARY) # Shared library version of libsamsung-ipc # ############################################ include $(CLEAR_VARS) +include $(LOCAL_PATH)/android_versions.mk LOCAL_MODULE := libsamsung-ipc LOCAL_MODULE_TAGS := optional @@ -149,25 +161,64 @@ LOCAL_EXPORT_C_INCLUDE_DIRS := $(local_export_c_include_dirs) include $(BUILD_SHARED_LIBRARY) +####################### +# https-send-sms tool # +####################### +include $(CLEAR_VARS) +include $(LOCAL_PATH)/android_versions.mk + +LOCAL_MODULE := https-send-sms +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := tools/https-send-sms.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/tools/include/glibc +LOCAL_SHARED_LIBRARIES := libcurl + +include $(BUILD_EXECUTABLE) + ################## # ipc-modem tool # ################## include $(CLEAR_VARS) +include $(LOCAL_PATH)/android_versions.mk LOCAL_MODULE := ipc-modem LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := tools/ipc-modem.c +LOCAL_SRC_FILES := \ + tools/ipc-modem/ipc-modem.c \ + tools/ipc-modem/ipc-modem-log.c -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/tools/include/glibc LOCAL_SHARED_LIBRARIES := libsamsung-ipc include $(BUILD_EXECUTABLE) +######################### +# ipc-modem-static tool # +######################### +include $(CLEAR_VARS) +include $(LOCAL_PATH)/android_versions.mk + +LOCAL_MODULE := ipc-modem-static +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := \ + tools/ipc-modem/ipc-modem.c \ + tools/ipc-modem/ipc-modem-log.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/tools/include/glibc +LOCAL_STATIC_LIBRARIES := libsamsung-ipc +LOCAL_SHARED_LIBRARIES := $(libsamsung_ipc_local_shared_libraries) + +include $(BUILD_EXECUTABLE) + ################# # ipc-test tool # ################# include $(CLEAR_VARS) +include $(LOCAL_PATH)/android_versions.mk LOCAL_MODULE := ipc-test LOCAL_MODULE_TAGS := optional @@ -180,17 +231,35 @@ LOCAL_SHARED_LIBRARIES := libsamsung-ipc include $(BUILD_EXECUTABLE) +##################### +# nv_data-imei tool # +##################### +include $(CLEAR_VARS) +include $(LOCAL_PATH)/android_versions.mk + +LOCAL_MODULE := nv_data-imei +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := tools/nv_data-imei.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/tools/include/glibc + +LOCAL_SHARED_LIBRARIES := libsamsung-ipc + +include $(BUILD_EXECUTABLE) + #################### # nv_data-md5 tool # #################### include $(CLEAR_VARS) +include $(LOCAL_PATH)/android_versions.mk LOCAL_MODULE := nv_data-md5 LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := tools/nv_data-md5.c -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/tools/include/glibc LOCAL_SHARED_LIBRARIES := libsamsung-ipc diff --git a/CONTRIBUTING b/CONTRIBUTING index f032039..8f9e7a1 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -7,7 +7,7 @@ https://git.replicant.us/replicant/hardware_replicant_libsamsung-ipc Policies -------- While at the time of writing, Replicant is the main user of libsamsung-ipc, and -maintain it, libsamsung-ipc usage is not limited to Replicant nor Android. +maintains it, libsamsung-ipc usage is not limited to Replicant nor Android. In the past, libsamsung-ipc was used in the SHR GNU/Linux distribution as well, with the freesmartphone.org middleware. diff --git a/MAINTAINERS b/MAINTAINERS index f3053b8..51088ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2,7 +2,7 @@ Maintainers List ---------------- M: Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> L: replicant@osuosl.org -T: git git://git.replicant.us/replicant/external_libsamsung-ipc.git +T: git https://git.replicant.us/replicant/hardware_replicant_libsamsung-ipc.git S: Maintained F: * F: */ diff --git a/Makefile.am b/Makefile.am index f036c81..376f52e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,17 +1,27 @@ NULL = +EXTRA_DIST = $(NULL) + SUBDIRS = \ samsung-ipc \ + samsung-ipc/tests \ include \ tools \ + tools/ipc-modem \ $(NULL) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = samsung-ipc.pc -EXTRA_DIST = \ +EXTRA_DIST += \ + Android.mk \ + android_versions.mk \ + CONTRIBUTING \ + INSTALL \ MAINTAINERS \ samsung-ipc.pc \ + strict-cflags.sh \ + tools/include/glibc/sysexits.h \ $(NULL) MAINTAINERCLEANFILES = \ @@ -20,3 +30,75 @@ MAINTAINERCLEANFILES = \ Makefile.in missing config.h.in \ mkinstalldirs *~ \ $(NULL) + +################################################################################ +# Extra checks # +################################################################################ + +if WANT_CODE_COVERAGE +check-local: build-code-coverage check-tarball-builds +install-data-local: install-code-coverage +clean-local: clean-code-coverage +else # WANT_CODE_COVERAGE +check-local: check-tarball-builds +endif # WANT_CODE_COVERAGE + +# With the next release of libsamsung-ipc, we will also release +# tarballs. +# +# Until now, no automatic testing was done with the libsamsung-ipc +# tarballs (all tests were done with the libsamsung-ipc git +# repository). +# +# We also need to verify that everything also builds and that the +# tests also work with tarballs. This can spot mistakes where people +# add new files that are not automatically picked up by automake and +# forget to add these files to EXTRA_DIST. +# +# This test runs only from git because it is not necessary with a +# tarball build. If we wanted to run it anyway we would end up in an +# infinite recursion as a given test from a tarball would then spawn +# another build and test from a tarball which would then spawn again +# another build and test. +check-tarball-builds: dist + if [ -d $(srcdir)/.git ] ; then \ + BUILD_TMPDIR=`mktemp -d` && \ + echo $$BUILD_TMPDIR && \ + mkdir $$BUILD_TMPDIR/build $$BUILD_TMPDIR/destdir && \ + tar xf $(srcdir)/libsamsung-ipc-$(VERSION).tar.xz \ + -C $$BUILD_TMPDIR && \ + cd $$BUILD_TMPDIR/build && \ + ../libsamsung-ipc-$(VERSION)/configure \ + $(CONFIGURE_ARGUMENTS) && \ + make check && \ + make install DESTDIR=`realpath $$BUILD_TMPDIR/destdir` && \ + rm -rf $$BUILD_TMPDIR ; \ + fi + +################# +# Code coverage # +################# +if WANT_CODE_COVERAGE + +CODE_COVERAGE_DIR = lcov + +EXTRA_DIST += $(CODE_COVERAGE_DIR) + +build-code-coverage: + lcov \ + --capture \ + --compat-libtool \ + --directory . \ + --output-file libsamsung-ipc.info + genhtml -o $(CODE_COVERAGE_DIR) libsamsung-ipc.info + +install-code-coverage: check + install -d $(DESTDIR)/$(datadir)/libsamsung-ipc/$(CODE_COVERAGE_DIR)/ + find $(srcdir)/$(CODE_COVERAGE_DIR) -type f | \ + xargs -I src install -D src \ + $(DESTDIR)/$(datadir)/libsamsung-ipc/$(CODE_COVERAGE_DIR)/ + +clean-code-coverage: + rm -rf $(CODE_COVERAGE_DIR) + +endif # WANT_CODE_COVERAGE @@ -7,10 +7,11 @@ found in many Samsung devices. Compilation and installation ============================ -In orderto compile the source code you need the following software packages +In order to compile the source code you need the following software packages - GCC compiler - GNC C library - OpenSSL >= 1.0.0e +- curl >= 7.62.0 To configure run ./configure --prefix=/usr diff --git a/android_versions.mk b/android_versions.mk new file mode 100644 index 0000000..f34ce57 --- /dev/null +++ b/android_versions.mk @@ -0,0 +1,39 @@ +# This file is part of libsamsung-ipc. +# +# Copyright (C) 2021 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> +# +# libsamsung-ipc is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# libsamsung-ipc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + +# We need to keep the compatibility with older Replicant versions, at least for +# testing purposes: some of the devices we support in libsamsung-ipc are not +# supported (yet) in newer Android versions or recent GNU/Linux distributions, +# and doing tests on older devices might enable us to also understand better +# the modem protocol and/or the EFS. + +# To limit the amount of work, we need to avoid modifying older Android versions +# to be able to use recent libsamsung-ril and libsamsung-ipc. + +# Here's how different Android distributions behave with +# LOCAL_PROPRIETARY_MODULE := true in libsamsung-ipc's Android.mk: +# Replicant 4.2: It probably breaks the modem support +# Replicant 6.0: It break the modem support +# Replicant 9.0: The modem integration doesn't work because it was not finished +# Replicant 10.0: The modem integration doesn't work because it was not finished +# Replicant 11.0: It is required for the modem to work + +# Set a default value for build systems like Guix +PLATFORM_VERSION ?= 7 +ifneq (1,$(filter 1,$(shell echo "$$(( $$(echo $(PLATFORM_VERSION) | sed 's/\..*//g') < 7 ))" ))) + LOCAL_PROPRIETARY_MODULE := true +endif diff --git a/configure.ac b/configure.ac index 9665a96..501a386 100644 --- a/configure.ac +++ b/configure.ac @@ -1,20 +1,16 @@ -AC_INIT([libsamsung-ipc], [0.6.0], [replicant@osuosl.org], [libsamsung-ipc]) +AC_INIT([libsamsung-ipc], [0.7.0], [replicant@osuosl.org], [libsamsung-ipc]) + +AC_SUBST(CONFIGURE_ARGUMENTS) +: ${CONFIGURE_ARGUMENTS="$@"} + AC_CONFIG_SRCDIR([Makefile.am]) AC_CONFIG_HEADERS(config.h) -AM_INIT_AUTOMAKE([dist-bzip2 subdir-objects]) +AM_INIT_AUTOMAKE([dist-bzip2 dist-xz subdir-objects]) +XZ_OPT=-v9e m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -AC_PROG_CC -AM_PROG_CC_C_O -AC_GNU_SOURCE -AC_DISABLE_STATIC -AC_PROG_LIBTOOL - -AC_SUBST(CFLAGS) -AC_SUBST(CPPFLAGS) -AC_SUBST(LDFLAGS) - OPENSSL_REQUIRED=1.0.0e +LIBCURL_REQUIRED=7.62.0 #------------------------------------------------------------------------------ # pkg-config @@ -29,38 +25,181 @@ AC_SUBST(OPENSSL_CFLAGS) AC_SUBST(OPENSSL_LIBS) #------------------------------------------------------------------------------ +# strict cflags +AC_SUBST(STRICT_CFLAGS) + +#------------------------------------------------------------------------------ +# Static build of test utilities +AC_SUBST(ALL_STATIC_LDFLAGS) + +#------------------------------------------------------------------------------ +# python +AC_SUBST(PYTHON3) +AC_CHECK_PROG([PYTHON3], [python3], [python3]) +#------------------------------------------------------------------------------ +# code coverage +AC_SUBST(CODE_COVERAGE) +#------------------------------------------------------------------------------ +# valgrind +AC_SUBST(VALGRIND) + +#------------------------------------------------------------------------------ +# ipc device name +AC_SUBST([IPC_DEVICE_NAME], [all]) + +#------------------------------------------------------------------------------ +# libcurl +PKG_CHECK_MODULES(LIBCURL, libcurl >= $LIBCURL_REQUIRED) +AC_SUBST(LIBCURL_CFLAGS) +AC_SUBST(LIBCURL_LIBS) + +#------------------------------------------------------------------------------ # check for debugging AC_ARG_ENABLE(debug, - [ --enable-debug Enable debug build (default=disabled)], + [AS_HELP_STRING([--enable-debug], [Enable debug build (default=disabled)])], [debug=$enableval], [debug="no"]) AM_CONDITIONAL( [WANT_DEBUG], [test x"$debug" = x"yes"]) #------------------------------------------------------------------------------ +AC_ARG_ENABLE(static-progs, + [AS_HELP_STRING([--enable-static-progs], + [Build static (test) tools (default=disabled)])], + [static_progs=$enableval], + [static_progs="no"]) + +#------------------------------------------------------------------------------ +AC_ARG_ENABLE(strict-cflags, + [AS_HELP_STRING([--enable-strict-cflags], + [Build with strict cflags (default=disabled)])], + [strict_cflags=$enableval], + [strict_cflags="no"]) +AM_CONDITIONAL( [WANT_STRICT_CFLAGS], [test x"$strict_cflags" = x"yes"]) + +#------------------------------------------------------------------------------ +# TODO: GCC has --enable-checking= for extensive runtime checks and one of the +# available values is valgrind, so it would be a good idea to convert to +# it for consistency across free software projects. +AC_ARG_ENABLE( + [code-coverage-tests], + [AS_HELP_STRING([--enable-code-coverage-tests], + [Build with --coverage, -O0 and use lcov from PATH during + tests (default=disabled)])], + [code_coverage_tests=$enableval], + [code_coverage="no"]) + +AM_CONDITIONAL( [WANT_CODE_COVERAGE], [test x"$code_coverage_tests" = x"yes"]) + +AS_IF([test x"$code_coverage_tests" = x"yes"], + [AC_CHECK_PROG([CODE_COVERAGE], [lcov], [lcov]) + AS_IF( + [test x"$CODE_COVERAGE" = x""], + [AC_MSG_ERROR( + [code coverage tests are enabled but lcov was not found in PATH ($PATH)])])]) + +#------------------------------------------------------------------------------ +# TODO: GCC has --enable-checking= for extensive runtime checks and one of the +# available values is valgrind, so it would be a good idea to convert to +# it for consistency across free software projects. +AC_ARG_ENABLE( + [valgrind-tests], + [AS_HELP_STRING([--enable-valgrind-tests], + [Build with -ggdb3, -O0 and use valgrind from PATH during + tests (default=disabled)])], + [valgrind_tests=$enableval], + [valgrind_tests="no"]) + +AM_CONDITIONAL( [WANT_VALGRIND_CHECKING], [test x"$valgrind_tests" = x"yes"]) + +AS_IF([test x"$valgrind_tests" = x"yes"], + [AC_CHECK_PROG([VALGRIND], [valgrind], [valgrind]) + AS_IF( + [test x"$VALGRIND" = x""], + [AC_MSG_ERROR( + [valgrind tests are enabled but valgrind was not found in PATH ($PATH)])])]) +#------------------------------------------------------------------------------ +AC_ARG_ENABLE(samsung-ipc-device, + [ --enable-samsung-ipc-device=[DEVICE] + make libsamsung-ipc assumes that it runs on DEVICE + DEVICE={all,aries,crespo,galaxys2,herolte,i9300,maguro,n5100,n7100,piranha} (default=all)], + [AS_CASE([$enableval], + [aries|crespo|galaxys2|herolte|i9300|maguro|n5100|n7100|piranha], + [IPC_DEVICE_NAME=$enableval], + [AC_MSG_ERROR([Unknown device $enableval])])], + [IPC_DEVICE_NAME="all"]) + +AM_CONDITIONAL( [WANT_IPC_DEVICE_NAME], [test x"$IPC_DEVICE_NAME" != x"all"]) +#------------------------------------------------------------------------------ + AC_CONFIG_FILES([ Makefile samsung-ipc.pc include/Makefile samsung-ipc/Makefile + samsung-ipc/tests/Makefile tools/Makefile + tools/ipc-modem/Makefile ]) -AC_OUTPUT + #------------------------------------------------------------------------------ +AS_IF([test x"$static_progs" = x"yes"], + [ALL_STATIC_LDFLAGS=-all-static], []) + +AS_IF([test x"$strict_cflags" = x"yes"], + [STRICT_CFLAGS=`$srcdir/strict-cflags.sh`], []) + +AS_IF([test x"$valgrind_tests" = x"yes"], [: ${CFLAGS="-ggdb3 -O0"}], + [test x"$debug" = x"yes"], [: ${CFLAGS="-ggdb -O0"}], []) + +AS_IF([test x"$code_coverage_tests" = x"yes"], + [: ${LDFLAGS=" --coverage"}], []) + +AS_IF([test x"$code_coverage_tests" = x"yes"], + [ CFLAGS+=" --coverage"], []) + +AC_PROG_CC +AM_PROG_CC_C_O +AC_GNU_SOURCE +AC_DISABLE_STATIC +AC_PROG_LIBTOOL + +AC_SUBST(CFLAGS) +AC_SUBST(CPPFLAGS) +AC_SUBST(LDFLAGS) +AC_SUBST(ALL_STATIC_LDFLAGS) + +AC_OUTPUT +#------------------------------------------------------------------------------ # info echo "------------------------------------------------------------------------" echo "$PACKAGE_NAME $PACKAGE_VERSION" echo "------------------------------------------------------------------------" echo +echo "Compiler flags:" +echo +echo " CFLAGS..................: $CFLAGS $STRICT_CFLAGS" +echo " LDFLAGS.................: $LDFLAGS $ALL_STATIC_LDFLAGS" +echo +echo +echo "Interpreters paths:" +echo +echo " PYTHON3..................: $PYTHON3" +echo +echo echo "Configuration Options:" echo echo " debug build.............: $debug" echo +echo " device(s)...............: $IPC_DEVICE_NAME" +echo echo " prefix..................: $prefix" echo +echo " valgrind tests..........: $valgrind_tests" +echo +echo " code coverage tests.....: $code_coverage_tests" echo "------------------------------------------------------------------------" echo echo "Now type 'make' to compile and 'make install' to install this package." - diff --git a/include/samsung-ipc.h b/include/samsung-ipc.h index b5a8315..d9aa7c7 100644 --- a/include/samsung-ipc.h +++ b/include/samsung-ipc.h @@ -19,11 +19,14 @@ * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. */ -#include <sys/time.h> - #ifndef __SAMSUNG_IPC_H__ #define __SAMSUNG_IPC_H__ +#include <stddef.h> +#include <unistd.h> + +#include <sys/time.h> + /* * Values */ @@ -134,6 +137,8 @@ int ipc_seq_valid(unsigned char seq); const char *ipc_request_type_string(unsigned char type); const char *ipc_response_type_string(unsigned char type); const char *ipc_command_string(unsigned short command); +const char *ipc_group_string(unsigned char group); +const char *ipc_client_type_string(unsigned char client_type); int ipc_data_dump(struct ipc_client *client, const void *data, size_t size); void ipc_client_log_send(struct ipc_client *client, struct ipc_message *message, @@ -150,11 +155,15 @@ int ipc_rfs_header_setup(struct ipc_rfs_header *header, int ipc_rfs_message_setup(const struct ipc_rfs_header *header, struct ipc_message *message); +ssize_t data_read(struct ipc_client *client, int fd, void *buf, size_t count); +ssize_t data_write(struct ipc_client *client, int fd, const void *buf, + size_t count); void *file_data_read(struct ipc_client *client, const char *path, size_t size, size_t chunk_size, unsigned int offset); int file_data_write(struct ipc_client *client, const char *path, const void *data, size_t size, size_t chunk_size, unsigned int offset); +off_t file_data_size(struct ipc_client *client, const char *path); int network_iface_up(const char *iface, int domain, int type); int network_iface_down(const char *iface, int domain, int type); int sysfs_value_read(const char *path); diff --git a/samsung-ipc/Makefile.am b/samsung-ipc/Makefile.am index 87becdd..47484e5 100644 --- a/samsung-ipc/Makefile.am +++ b/samsung-ipc/Makefile.am @@ -6,8 +6,16 @@ AM_CFLAGS = \ $(OPENSSL_CFLAGS) \ $(NULL) +if WANT_STRICT_CFLAGS +AM_CFLAGS += $(STRICT_CFLAGS) +endif + if WANT_DEBUG -AM_CFLAGS += -ggdb -O0 -DDEBUG +AM_CFLAGS += -DDEBUG +endif + +if WANT_IPC_DEVICE_NAME +AM_CFLAGS += -DIPC_DEVICE_NAME=\"$(IPC_DEVICE_NAME)\" endif lib_LTLIBRARIES = \ @@ -17,41 +25,8 @@ lib_LTLIBRARIES = \ libsamsung_ipc_la_SOURCES = \ ipc.c \ ipc.h \ + ipc_strings.c \ ipc_utils.c \ - modems/xmm616/xmm616.c \ - modems/xmm616/xmm616.h \ - modems/xmm626/xmm626.c \ - modems/xmm626/xmm626.h \ - modems/xmm626/xmm626_hsic.c \ - modems/xmm626/xmm626_hsic.h \ - modems/xmm626/xmm626_kernel_smdk4412.c \ - modems/xmm626/xmm626_kernel_smdk4412.h \ - modems/xmm626/xmm626_mipi.c \ - modems/xmm626/xmm626_mipi.h \ - modems/xmm626/xmm626_modem_if.h \ - modems/xmm626/xmm626_modem_link_device_hsic.h \ - modems/xmm626/xmm626_modem_prj.h \ - devices/ipc_devices.c \ - devices/ipc_devices.h \ - devices/crespo/crespo.c \ - devices/crespo/crespo.h \ - devices/crespo/crespo_modem_ctl.h \ - devices/aries/aries.c \ - devices/aries/aries.h \ - devices/aries/onedram.h \ - devices/aries/phonet.h \ - devices/galaxys2/galaxys2.c \ - devices/galaxys2/galaxys2.h \ - devices/maguro/maguro.c \ - devices/maguro/maguro.h \ - devices/piranha/piranha.c \ - devices/piranha/piranha.h \ - devices/i9300/i9300.c \ - devices/i9300/i9300.h \ - devices/n7100/n7100.c \ - devices/n7100/n7100.h \ - devices/n5100/n5100.c \ - devices/n5100/n5100.h \ utils.c \ call.c \ sms.c \ @@ -64,6 +39,10 @@ libsamsung_ipc_la_SOURCES = \ gen.c \ $(NULL) +include devices/Makefile.am +include modems/Makefile.am +include partitions/Makefile.am + libsamsung_ipc_la_LIBADD = \ $(OPENSSL_LIBS) \ $(NULL) diff --git a/samsung-ipc/devices/Makefile.am b/samsung-ipc/devices/Makefile.am new file mode 100644 index 0000000..8257be3 --- /dev/null +++ b/samsung-ipc/devices/Makefile.am @@ -0,0 +1,25 @@ +libsamsung_ipc_la_SOURCES += \ + devices/ipc_devices.c \ + devices/ipc_devices.h \ + devices/crespo/crespo.c \ + devices/crespo/crespo.h \ + devices/crespo/crespo_modem_ctl.h \ + devices/aries/aries.c \ + devices/aries/aries.h \ + devices/aries/onedram.h \ + devices/aries/phonet.h \ + devices/galaxys2/galaxys2.c \ + devices/galaxys2/galaxys2.h \ + devices/maguro/maguro.c \ + devices/maguro/maguro.h \ + devices/piranha/piranha.c \ + devices/piranha/piranha.h \ + devices/i9300/i9300.c \ + devices/i9300/i9300.h \ + devices/n7100/n7100.c \ + devices/n7100/n7100.h \ + devices/n5100/n5100.c \ + devices/n5100/n5100.h \ + devices/herolte/herolte.c \ + devices/herolte/herolte.h \ + $(NULL) diff --git a/samsung-ipc/devices/aries/aries.c b/samsung-ipc/devices/aries/aries.c index 93d5d31..9fc905a 100644 --- a/samsung-ipc/devices/aries/aries.c +++ b/samsung-ipc/devices/aries/aries.c @@ -819,11 +819,16 @@ char *aries_gprs_get_iface(__attribute__((unused)) struct ipc_client *client, unsigned int cid) { char *iface = NULL; + int rc; if (cid > ARIES_GPRS_IFACE_COUNT) return NULL; - asprintf(&iface, "%s%d", ARIES_GPRS_IFACE_PREFIX, cid - 1); + rc = asprintf(&iface, "%s%d", ARIES_GPRS_IFACE_PREFIX, cid - 1); + if (rc == -1) { + ipc_client_log(client, "%s: asprintf failed", __func__); + return NULL; + } return iface; } diff --git a/samsung-ipc/devices/crespo/crespo.c b/samsung-ipc/devices/crespo/crespo.c index ebb6075..5399e77 100644 --- a/samsung-ipc/devices/crespo/crespo.c +++ b/samsung-ipc/devices/crespo/crespo.c @@ -553,13 +553,17 @@ int crespo_gprs_deactivate(__attribute__((unused)) struct ipc_client *client, return 0; } -char *crespo_gprs_get_iface_single( - __attribute__((unused)) struct ipc_client *client, - __attribute__((unused)) unsigned int cid) +char *crespo_gprs_get_iface_single(struct ipc_client *client, + __attribute__((unused)) unsigned int cid) { char *iface = NULL; + int rc; - asprintf(&iface, "%s%d", CRESPO_GPRS_IFACE_PREFIX, 0); + rc = asprintf(&iface, "%s%d", CRESPO_GPRS_IFACE_PREFIX, 0); + if (rc == -1) { + ipc_client_log(client, "%s: asprintf failed", __func__); + return NULL; + } return iface; } @@ -580,11 +584,16 @@ char *crespo_gprs_get_iface(__attribute__((unused)) struct ipc_client *client, unsigned int cid) { char *iface = NULL; + int rc; if (cid > CRESPO_GPRS_IFACE_COUNT) return NULL; - asprintf(&iface, "%s%d", CRESPO_GPRS_IFACE_PREFIX, cid - 1); + rc = asprintf(&iface, "%s%d", CRESPO_GPRS_IFACE_PREFIX, cid - 1); + if (rc == -1) { + ipc_client_log(client, "%s: asprintf failed", __func__); + return NULL; + } return iface; } diff --git a/samsung-ipc/devices/galaxys2/galaxys2.c b/samsung-ipc/devices/galaxys2/galaxys2.c index cdfe20c..954417f 100644 --- a/samsung-ipc/devices/galaxys2/galaxys2.c +++ b/samsung-ipc/devices/galaxys2/galaxys2.c @@ -78,29 +78,59 @@ int galaxys2_boot(struct ipc_client *client) ipc_client_log(client, "Opened modem link device"); rc = xmm626_kernel_smdk4412_power(client, modem_boot_fd, 0); - rc |= xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, - 0); - rc |= xmm626_kernel_smdk4412_hci_power(client, 0); - rc |= xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, - 0); + if (rc < 0) { + ipc_client_log(client, "Powering off the modem failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, + 0); + if (rc < 0) { + ipc_client_log(client, "Disabling the modem link failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 0); if (rc < 0) { - ipc_client_log(client, "Turning the modem off failed"); + ipc_client_log(client, "Powering off the HCI bus failed"); goto error; } + + rc = xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, + 0); + if (rc < 0) { + ipc_client_log(client, "Deactivating the modem link failed"); + goto error; + } + ipc_client_log(client, "Turned the modem off"); rc = xmm626_kernel_smdk4412_power(client, modem_boot_fd, 1); - rc |= xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, - 1); - rc |= xmm626_kernel_smdk4412_hci_power(client, 1); - rc |= xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, - 1); + if (rc < 0) { + ipc_client_log(client, "Powering on the modem failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, + 1); + if (rc < 0) { + ipc_client_log(client, "Enabling the modem link failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_hci_power(client, 1); + if (rc < 0) { + ipc_client_log(client, "Powering on the HCI bus failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, + 1); if (rc < 0) { - ipc_client_log(client, "Turning the modem on failed"); + ipc_client_log(client, "Activating the modem link failed"); goto error; } + ipc_client_log(client, "Turned the modem on"); rc = xmm626_kernel_smdk4412_link_connected_wait(client, modem_link_fd); @@ -188,12 +218,21 @@ int galaxys2_boot(struct ipc_client *client) rc = xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, 0); - rc |= xmm626_kernel_smdk4412_hci_power(client, 0); - rc |= xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, - 0); + if (rc < 0) { + ipc_client_log(client, "Disabling the modem link failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 0); if (rc < 0) { - ipc_client_log(client, "Turning the modem off failed"); + ipc_client_log(client, "Powering off the HCI bus failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, + 0); + if (rc < 0) { + ipc_client_log(client, "Deactivating the modem link failed"); goto error; } @@ -207,12 +246,21 @@ int galaxys2_boot(struct ipc_client *client) rc = xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, 1); - rc |= xmm626_kernel_smdk4412_hci_power(client, 1); - rc |= xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, - 1); + if (rc < 0) { + ipc_client_log(client, "Enabling the modem link failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 1); + if (rc < 0) { + ipc_client_log(client, "Powering on the HCI bus failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, + 1); if (rc < 0) { - ipc_client_log(client, "Turning the modem on failed"); + ipc_client_log(client, "Activating the modem link failed"); goto error; } diff --git a/samsung-ipc/devices/herolte/herolte.c b/samsung-ipc/devices/herolte/herolte.c new file mode 100644 index 0000000..c5d15ef --- /dev/null +++ b/samsung-ipc/devices/herolte/herolte.c @@ -0,0 +1,610 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2013-2014 Paul Kocialkowski <contact@paulk.fr> + * Copyright (C) 2017 Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> + * Copyright (C) 2020 Tony Garnock-Jones <tonyg@leastfixedpoint.com> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "devices/herolte/herolte.h" +#include "ipc.h" +#include "modems/xmm626/xmm626.h" +#include "modems/xmm626/xmm626_kernel_smdk4412.h" +#include "modems/xmm626/xmm626_modem_prj.h" +#include "partitions/android/android.h" +#include "partitions/toc/toc.h" + +struct __attribute__((__packed__)) security_req { + uint32_t mode; + uint32_t size_boot; + uint32_t size_main; + uint32_t pad_zero; +}; + +struct __attribute__((__packed__)) modem_firmware_partition_data { + uint8_t *binary; + uint32_t size; + uint32_t m_offset; + uint32_t b_offset; + uint32_t mode; + ssize_t len; +}; + +#define IOCTL_SECURITY_REQ _IO('o', 0x53) + +#define MAX_CHUNK_LEN (62 * 1024) /* This is just what cbd uses. + * Perhaps a larger value would also work. + */ + +static int upload_chunk(struct ipc_client *client, + int device_fd, + int firmware_fd, + struct firmware_toc_entry const *toc, + char const *name, + uint32_t *size) +{ + int rc = -1; + struct modem_firmware_partition_data partition; + struct firmware_toc_entry const *boot_toc_entry; + struct firmware_toc_entry const *current_toc_entry; + uint32_t remaining; + + ipc_client_log(client, "Uploading %s", name); + + boot_toc_entry = find_toc_entry("BOOT", toc); + if (boot_toc_entry == NULL) { + ipc_client_log(client, + "%s: Failed to find BOOT entry in the TOC", + __func__); + goto exit; + } + + current_toc_entry = find_toc_entry(name, toc); + if (current_toc_entry == NULL) { + ipc_client_log(client, "%s: Failed to find %s entry in the TOC", + __func__, name); + goto exit; + } + + if (size != NULL) + *size = current_toc_entry->size; + ipc_client_log(client, " - blob size for %s is %lu", name, + current_toc_entry->size); + + partition.binary = calloc(1, MAX_CHUNK_LEN); + if (partition.binary == NULL) { + rc = -errno; + ipc_client_log(client, "%s: calloc failed with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + + partition.size = current_toc_entry->size; + partition.m_offset = current_toc_entry->loadaddr - + boot_toc_entry->loadaddr; + partition.b_offset = current_toc_entry->offset; + partition.mode = 0; + partition.len = 0; + + if (lseek(firmware_fd, partition.b_offset, SEEK_SET) < 0) { + rc = -errno; + ipc_client_log(client, "%s: lseek failed with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + + remaining = partition.size; + while (remaining > 0) { + partition.len = remaining < MAX_CHUNK_LEN ? + remaining : MAX_CHUNK_LEN; + if (data_read(client, firmware_fd, partition.binary, + partition.len) != partition.len) { + rc = errno; + ipc_client_log(client, + "%s: read failed with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + + if (ioctl(device_fd, IOCTL_DPRAM_SEND_BOOT, &partition) == -1) { + rc = errno; + ipc_client_log(client, + "%s: IOCTL_DPRAM_SEND_BOOT failed" + " with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + partition.m_offset += partition.len; + partition.b_offset += partition.len; + remaining -= partition.len; + } + + rc = 0; + +exit: + if (partition.binary != NULL) { + partition.binary = NULL; + free(partition.binary); + } + + return rc; +} + +static int select_secure_mode(struct ipc_client *client, + int boot0_fd, + int secure, + uint32_t size_boot, + uint32_t size_main) +{ + struct security_req req; + int rc; + + ipc_client_log(client, + "Issuing IOCTL_SECURITY_REQ - setting %s mode", + secure ? "secure" : "insecure"); + + req.mode = secure ? 0 : 2; + req.size_boot = size_boot; + req.size_main = size_main; + req.pad_zero = 0; + + if (ioctl(boot0_fd, IOCTL_SECURITY_REQ, &req) == -1) { + rc = errno; + ipc_client_log(client, + "%s: " + "IOCTL_SECURITY_REQ failed with error %d: %s", + __func__, rc, strerror(rc)); + return -1; + } + + return 0; +} + +static char const * const modem_image_devices[] = { + "/dev/disk/by-partlabel/RADIO", /* PostmarketOS */ + "/dev/block/platform/155a0000.ufs/by-name/RADIO", /* LineageOS */ + NULL +}; + +int herolte_boot(struct ipc_client *client) +{ + struct firmware_toc_entry toc[N_TOC_ENTRIES]; + int boot0_fd = -1; + int imagefd = -1; + int nvfd = -1; + int rc = -1; + uint32_t size_boot; + uint32_t size_main; + + ipc_client_log(client, "Loading firmware TOC"); + + imagefd = open_android_modem_partition(client, modem_image_devices); + if (imagefd == -1) { + rc = errno; + if (rc == ENOENT) + ipc_client_log(client, + "%s: no modem image block device found!", + __func__); + else + ipc_client_log(client, + "%s: " + "open_image_device failed with error %d:" + " %s", + __func__, rc, strerror(rc)); + goto exit; + } + + if (data_read(client, imagefd, &toc[0], sizeof(toc)) != sizeof(toc)) { + rc = errno; + ipc_client_log(client, + "%s: read modem image block device failed " + " with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + + ipc_client_log(client, "Loaded firmware TOC"); + + nvfd = open(herolte_nv_data_specs.nv_data_path, O_RDONLY | O_NOCTTY); + if (nvfd == -1) { + rc = errno; + ipc_client_log(client, + "%s: opening %s failed with error %d: %s", + __func__, herolte_nv_data_specs.nv_data_path, + rc, strerror(rc)); + goto exit; + } + ipc_client_log(client, "Opened NV data file"); + + boot0_fd = open(XMM626_SEC_MODEM_BOOT0_DEVICE, O_RDWR | O_NOCTTY); + if (boot0_fd < 0) { + rc = errno; + ipc_client_log(client, + "%s: opening %s failed with error %d: %s", + __func__, XMM626_SEC_MODEM_BOOT0_DEVICE, + rc, strerror(rc)); + goto exit; + } + + ipc_client_log(client, "Resetting modem"); + if (ioctl(boot0_fd, IOCTL_MODEM_RESET, 0) == -1) { + rc = errno; + ipc_client_log(client, + "%s: " + "IOCTL_MODEM_RESET failed with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + + if (select_secure_mode(client, boot0_fd, 0, 0, 0) < 0) + goto exit; + + if (upload_chunk(client, boot0_fd, imagefd, toc, "BOOT", + &size_boot) < 0) + goto exit; + + if (upload_chunk(client, boot0_fd, imagefd, toc, "MAIN", + &size_main) < 0) + goto exit; + + if (upload_chunk(client, boot0_fd, nvfd, toc, "NV", NULL) < 0) + goto exit; + + if (select_secure_mode(client, boot0_fd, 1, size_boot, size_main) < 0) + goto exit; + + ipc_client_log(client, "Powering on modem"); + if (xmm626_kernel_smdk4412_power(client, boot0_fd, 1) == -1) { + ipc_client_log(client, "%s: Powering on modem failed", + __func__); + goto exit; + } + + ipc_client_log(client, "Starting modem boot process"); + if (xmm626_kernel_smdk4412_boot_power(client, boot0_fd, 1) == -1) { + ipc_client_log(client, "%s: Starting modem boot process failed", + __func__); + goto exit; + } + + ipc_client_log(client, "Kicking off firmware download"); + if (ioctl(boot0_fd, IOCTL_MODEM_DL_START, 0) < 0) { + rc = errno; + ipc_client_log(client, + "%s: " + "IOCTL_MODEM_RESET failed with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + + ipc_client_log(client, "Handshaking with modem"); + /* At this point, cbd engages in a little dance with the + * newly-booted modem, apparently to verify that it is running + * as expected. I don’t know the sources of these magic + * numbers, I just faithfully reproduce them. + */ + { + uint32_t buf; + + buf = 0x900d; + if (data_write(client, boot0_fd, &buf, sizeof(buf)) != + sizeof(buf)) { + rc = errno; + ipc_client_log(client, + "%s: write failed with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + if (data_read(client, boot0_fd, &buf, sizeof(buf)) != + sizeof(buf)) { + rc = errno; + ipc_client_log(client, + "%s: read failed with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + if (buf != 0xa00d) { + ipc_client_log(client, + "%s: Handshake stage I failed: " + "expected 0xa00d, got 0x%x instead", + __func__, buf); + goto exit; + } + ipc_client_log(client, "Handshake stage I passed"); + + buf = 0x9f00; + if (data_write(client, boot0_fd, &buf, + sizeof(buf)) != sizeof(buf)) { + rc = errno; + ipc_client_log(client, + "%s: write failed with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + if (data_read(client, boot0_fd, &buf, + sizeof(buf)) != sizeof(buf)) { + rc = errno; + ipc_client_log(client, + "%s: read failed with error %d: %s", + __func__, rc, strerror(rc)); + goto exit; + } + if (buf != 0xaf00) { + ipc_client_log(client, + "%s: Handshake stage II failed: " + "expected 0xaf00, got 0x%x instead", + __func__, buf); + goto exit; + } + ipc_client_log(client, "Handshake stage II passed"); + } + + ipc_client_log(client, "Finishing modem boot process"); + if (xmm626_kernel_smdk4412_boot_power(client, boot0_fd, 0) == -1) { + ipc_client_log(client, + "%s: xmm626_kernel_smdk4412_boot_power failed", + __func__); + goto exit; + } + + ipc_client_log(client, "Modem boot complete"); + rc = 0; + + /* Samsung's official daemons continue to read from umts_boot0 + * (XMM626_SEC_MODEM_BOOT0_DEVICE) at this point. It may be + * done to restart the modem in case of errors. The fact that + * I have never seen anything actually come out of umts_boot0 + * after booting is complete with libsamsung-ipc seem to be + * consistent with that hypothesis. With libsamsung-ipc, + * it's up to the daemon using it (like libsamsung-ril) to restart + * the boot sequence if .poll (here herolte_poll) fails. + * For that to work, the kernel driver needs to return an error + * if the modem crash for instance. + */ + + /* The kernel modem driver for this device[1] checks if both the FMT + * and the RFS channels are open (in the rild_ready function) and will + * refuse to work if both channels aren't open. Note that even if + * libsamsung-ipc opens both channels, at the time of writing, none + * of the tools in tools/ opens the RFS channel. + * + * [1]See the rild_ready function and its usage in + * drivers/misc/modem_v1/link_device_shmem.c in the lineage-17.0 branch + * https://github.com/ivanmeler/android_kernel_samsung_herolte + */ + +exit: + if (boot0_fd != -1) + close(boot0_fd); + if (imagefd != -1) + close(imagefd); + if (nvfd != -1) + close(nvfd); + return rc; +} + +int herolte_open(__attribute__((unused)) struct ipc_client *client, void *data, + int type) +{ + struct herolte_transport_data *transport_data; + + if (data == NULL) + return -1; + + transport_data = (struct herolte_transport_data *) data; + + transport_data->fd = xmm626_kernel_smdk4412_open(client, type); + if (transport_data->fd < 0) + return -1; + + return 0; +} + +int herolte_close(__attribute__((unused)) struct ipc_client *client, void *data) +{ + struct herolte_transport_data *transport_data; + + if (data == NULL) + return -1; + + transport_data = (struct herolte_transport_data *) data; + + xmm626_kernel_smdk4412_close(client, transport_data->fd); + transport_data->fd = -1; + + return 0; +} + +int herolte_read(__attribute__((unused)) struct ipc_client *client, void *data, + void *buffer, size_t length) +{ + struct herolte_transport_data *transport_data; + int rc; + + if (data == NULL) + return -1; + + transport_data = (struct herolte_transport_data *) data; + + rc = xmm626_kernel_smdk4412_read(client, transport_data->fd, buffer, + length); + + return rc; +} + +int herolte_write(__attribute__((unused)) struct ipc_client *client, void *data, + const void *buffer, size_t length) +{ + struct herolte_transport_data *transport_data; + int rc; + + if (data == NULL) + return -1; + + transport_data = (struct herolte_transport_data *) data; + + rc = xmm626_kernel_smdk4412_write(client, transport_data->fd, buffer, + length); + + return rc; +} + +int herolte_poll(__attribute__((unused)) struct ipc_client *client, void *data, + struct ipc_poll_fds *fds, struct timeval *timeout) +{ + struct herolte_transport_data *transport_data; + int rc; + + if (data == NULL) + return -1; + + transport_data = (struct herolte_transport_data *) data; + + rc = xmm626_kernel_smdk4412_poll(client, transport_data->fd, fds, + timeout); + + return rc; +} + +int herolte_power_on(__attribute__((unused)) struct ipc_client *client, + __attribute__((unused)) void *data) +{ + return 0; +} + +int herolte_power_off(__attribute__((unused)) struct ipc_client *client, + __attribute__((unused)) void *data) +{ + int fd; + int rc; + + fd = open(XMM626_SEC_MODEM_BOOT0_DEVICE, O_RDWR | O_NOCTTY | + O_NONBLOCK); + if (fd < 0) + return -1; + + rc = xmm626_kernel_smdk4412_power(client, fd, 0); + + close(fd); + + if (rc < 0) + return -1; + + return 0; +} + +int herolte_gprs_activate(__attribute__((unused)) struct ipc_client *client, + __attribute__((unused)) void *data, + __attribute__((unused)) unsigned int cid) +{ + /* TODO: For now, we don't have enough information to + * implement this sensibly, hence this placeholder. + */ + + return 0; +} + +int herolte_gprs_deactivate(__attribute__((unused)) struct ipc_client *client, + __attribute__((unused)) void *data, + __attribute__((unused)) unsigned int cid) +{ + /* TODO: For now, we don't have enough information to + * implement this sensibly, hence this placeholder. + */ + + return 0; +} + +int herolte_data_create(__attribute__((unused)) struct ipc_client *client, + void **transport_data, + __attribute__((unused)) void **power_data, + __attribute__((unused)) void **gprs_data) +{ + if (transport_data == NULL) + return -1; + + *transport_data = calloc(1, sizeof(struct herolte_transport_data)); + + return 0; +} + +int herolte_data_destroy(__attribute__((unused)) struct ipc_client *client, + void *transport_data, + __attribute__((unused)) void *power_data, + __attribute__((unused)) void *gprs_data) +{ + if (transport_data == NULL) + return -1; + + free(transport_data); + + return 0; +} + +struct ipc_client_ops herolte_fmt_ops = { + .boot = herolte_boot, + .send = xmm626_kernel_smdk4412_fmt_send, + .recv = xmm626_kernel_smdk4412_fmt_recv, +}; + +struct ipc_client_ops herolte_rfs_ops = { + .boot = NULL, + .send = xmm626_kernel_smdk4412_rfs_send, + .recv = xmm626_kernel_smdk4412_rfs_recv, +}; + +struct ipc_client_handlers herolte_handlers = { + .read = herolte_read, + .write = herolte_write, + .open = herolte_open, + .close = herolte_close, + .poll = herolte_poll, + .transport_data = NULL, + .power_on = herolte_power_on, + .power_off = herolte_power_off, + .power_data = NULL, + .gprs_activate = herolte_gprs_activate, + .gprs_deactivate = herolte_gprs_deactivate, + .gprs_data = NULL, + .data_create = herolte_data_create, + .data_destroy = herolte_data_destroy, +}; + +struct ipc_client_gprs_specs herolte_gprs_specs = { + .gprs_get_iface = xmm626_kernel_smdk4412_gprs_get_iface, + .gprs_get_capabilities = xmm626_kernel_smdk4412_gprs_get_capabilities, +}; + +struct ipc_client_nv_data_specs herolte_nv_data_specs = { + .nv_data_path = XMM626_NV_DATA_PATH, + .nv_data_md5_path = XMM626_NV_DATA_MD5_PATH, + .nv_data_backup_path = XMM626_NV_DATA_BACKUP_PATH, + .nv_data_backup_md5_path = XMM626_NV_DATA_BACKUP_MD5_PATH, + .nv_data_secret = XMM626_NV_DATA_SECRET, + .nv_data_size = XMM626_NV_DATA_SIZE, + .nv_data_chunk_size = XMM626_NV_DATA_CHUNK_SIZE, +}; diff --git a/samsung-ipc/devices/herolte/herolte.h b/samsung-ipc/devices/herolte/herolte.h new file mode 100644 index 0000000..32d1638 --- /dev/null +++ b/samsung-ipc/devices/herolte/herolte.h @@ -0,0 +1,35 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2013-2014 Paul Kocialkowski <contact@paulk.fr> + * Copyright (C) 2017 Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> + * Copyright (C) 2020 Tony Garnock-Jones <tonyg@leastfixedpoint.com> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __HEROLTE_H__ +#define __HEROLTE_H__ + +struct herolte_transport_data { + int fd; +}; + +extern struct ipc_client_ops herolte_fmt_ops; +extern struct ipc_client_ops herolte_rfs_ops; +extern struct ipc_client_handlers herolte_handlers; +extern struct ipc_client_gprs_specs herolte_gprs_specs; +extern struct ipc_client_nv_data_specs herolte_nv_data_specs; + +#endif /* __HEROLTE_H__ */ diff --git a/samsung-ipc/devices/i9300/i9300.c b/samsung-ipc/devices/i9300/i9300.c index 85709a8..a2c2b92 100644 --- a/samsung-ipc/devices/i9300/i9300.c +++ b/samsung-ipc/devices/i9300/i9300.c @@ -85,10 +85,14 @@ int i9300_boot(struct ipc_client *client) ipc_client_log(client, "Turned the modem off"); rc = xmm626_kernel_smdk4412_power(client, modem_boot_fd, 1); - rc |= xmm626_kernel_smdk4412_hci_power(client, 1); + if (rc < 0) { + ipc_client_log(client, "Powering on the modem failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 1); if (rc < 0) { - ipc_client_log(client, "Turning the modem on failed"); + ipc_client_log(client, "Powering on the HCI bus failed"); goto error; } ipc_client_log(client, "Turned the modem on"); @@ -178,12 +182,21 @@ int i9300_boot(struct ipc_client *client) rc = xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, 0); - rc |= xmm626_kernel_smdk4412_hci_power(client, 0); - rc |= xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, - 0); + if (rc < 0) { + ipc_client_log(client, "Disabling the modem link failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 0); if (rc < 0) { - ipc_client_log(client, "Turning the modem off failed"); + ipc_client_log(client, "Powering off the HCI bus failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, + 0); + if (rc < 0) { + ipc_client_log(client, "Deactivating the modem link failed"); goto error; } @@ -197,12 +210,21 @@ int i9300_boot(struct ipc_client *client) rc = xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, 1); - rc |= xmm626_kernel_smdk4412_hci_power(client, 1); - rc |= xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, - 1); + if (rc < 0) { + ipc_client_log(client, "Enabling the modem link failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 1); + if (rc < 0) { + ipc_client_log(client, "Powering on the HCI bus failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, + 1); if (rc < 0) { - ipc_client_log(client, "Turning the modem on failed"); + ipc_client_log(client, "Activating the modem link failed"); goto error; } diff --git a/samsung-ipc/devices/ipc_devices.c b/samsung-ipc/devices/ipc_devices.c index 91663f6..a0fe955 100644 --- a/samsung-ipc/devices/ipc_devices.c +++ b/samsung-ipc/devices/ipc_devices.c @@ -154,6 +154,16 @@ struct ipc_device_desc ipc_devices[] = { .gprs_specs = &n5100_gprs_specs, .nv_data_specs = &n5100_nv_data_specs, }, + { + .name = "herolte", + .board_name = NULL, + .kernel_version = NULL, + .fmt_ops = &herolte_fmt_ops, + .rfs_ops = &herolte_rfs_ops, + .handlers = &herolte_handlers, + .gprs_specs = &herolte_gprs_specs, + .nv_data_specs = &herolte_nv_data_specs, + }, }; unsigned int ipc_devices_count = sizeof(ipc_devices) / diff --git a/samsung-ipc/devices/ipc_devices.h b/samsung-ipc/devices/ipc_devices.h index 176607c..8160ee1 100644 --- a/samsung-ipc/devices/ipc_devices.h +++ b/samsung-ipc/devices/ipc_devices.h @@ -20,14 +20,15 @@ #include <samsung-ipc.h> -#include "devices/crespo/crespo.h" #include "devices/aries/aries.h" +#include "devices/crespo/crespo.h" #include "devices/galaxys2/galaxys2.h" -#include "devices/maguro/maguro.h" -#include "devices/piranha/piranha.h" +#include "devices/herolte/herolte.h" #include "devices/i9300/i9300.h" -#include "devices/n7100/n7100.h" +#include "devices/maguro/maguro.h" #include "devices/n5100/n5100.h" +#include "devices/n7100/n7100.h" +#include "devices/piranha/piranha.h" #ifndef __IPC_DEVICES_H__ #define __IPC_DEVICES_H__ diff --git a/samsung-ipc/devices/n5100/n5100.c b/samsung-ipc/devices/n5100/n5100.c index 0abe896..f7055bf 100644 --- a/samsung-ipc/devices/n5100/n5100.c +++ b/samsung-ipc/devices/n5100/n5100.c @@ -86,10 +86,14 @@ int n5100_boot(struct ipc_client *client) ipc_client_log(client, "Turned the modem off"); rc = xmm626_kernel_smdk4412_power(client, modem_boot_fd, 1); - rc |= xmm626_kernel_smdk4412_hci_power(client, 1); + if (rc < 0) { + ipc_client_log(client, "Powering on the modem failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 1); if (rc < 0) { - ipc_client_log(client, "Turning the modem on failed"); + ipc_client_log(client, "Powering on the HCI bus failed"); goto error; } ipc_client_log(client, "Turned the modem on"); @@ -179,12 +183,21 @@ int n5100_boot(struct ipc_client *client) rc = xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, 0); - rc |= xmm626_kernel_smdk4412_hci_power(client, 0); - rc |= xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, - 0); + if (rc < 0) { + ipc_client_log(client, "Disabling the modem link failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 0); if (rc < 0) { - ipc_client_log(client, "Turning the modem off failed"); + ipc_client_log(client, "Powering off the HCI bus failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, + 0); + if (rc < 0) { + ipc_client_log(client, "Deactivating the modem link failed"); goto error; } @@ -198,12 +211,21 @@ int n5100_boot(struct ipc_client *client) rc = xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, 1); - rc |= xmm626_kernel_smdk4412_hci_power(client, 1); - rc |= xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, - 1); + if (rc < 0) { + ipc_client_log(client, "Enabling the modem link failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 1); + if (rc < 0) { + ipc_client_log(client, "Powering on the HCI bus failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, + 1); if (rc < 0) { - ipc_client_log(client, "Turning the modem on failed"); + ipc_client_log(client, "Activating the modem link failed"); goto error; } diff --git a/samsung-ipc/devices/n7100/n7100.c b/samsung-ipc/devices/n7100/n7100.c index d5091a2..6312c18 100644 --- a/samsung-ipc/devices/n7100/n7100.c +++ b/samsung-ipc/devices/n7100/n7100.c @@ -85,10 +85,14 @@ int n7100_boot(struct ipc_client *client) ipc_client_log(client, "Turned the modem off"); rc = xmm626_kernel_smdk4412_power(client, modem_boot_fd, 1); - rc |= xmm626_kernel_smdk4412_hci_power(client, 1); + if (rc < 0) { + ipc_client_log(client, "Powering on the modem failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 1); if (rc < 0) { - ipc_client_log(client, "Turning the modem on failed"); + ipc_client_log(client, "Powering on the HCI bus failed"); goto error; } ipc_client_log(client, "Turned the modem on"); @@ -178,12 +182,21 @@ int n7100_boot(struct ipc_client *client) rc = xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, 0); - rc |= xmm626_kernel_smdk4412_hci_power(client, 0); - rc |= xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, - 0); + if (rc < 0) { + ipc_client_log(client, "Disabling the modem link failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 0); if (rc < 0) { - ipc_client_log(client, "Turning the modem off failed"); + ipc_client_log(client, "Powering off the HCI bus failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, + 0); + if (rc < 0) { + ipc_client_log(client, "Deactivating the modem link failed"); goto error; } @@ -197,12 +210,21 @@ int n7100_boot(struct ipc_client *client) rc = xmm626_kernel_smdk4412_link_control_enable(client, modem_link_fd, 1); - rc |= xmm626_kernel_smdk4412_hci_power(client, 1); - rc |= xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, - 1); + if (rc < 0) { + ipc_client_log(client, "Enabling the modem link failed"); + goto error; + } + rc = xmm626_kernel_smdk4412_hci_power(client, 1); + if (rc < 0) { + ipc_client_log(client, "Powering on the HCI bus failed"); + goto error; + } + + rc = xmm626_kernel_smdk4412_link_control_active(client, modem_link_fd, + 1); if (rc < 0) { - ipc_client_log(client, "Turning the modem on failed"); + ipc_client_log(client, "Activating the modem link failed"); goto error; } diff --git a/samsung-ipc/gprs.c b/samsung-ipc/gprs.c index 89f06e7..c9722e5 100644 --- a/samsung-ipc/gprs.c +++ b/samsung-ipc/gprs.c @@ -35,7 +35,7 @@ int ipc_gprs_define_pdp_context_setup( data->cid = cid; data->magic = 0x02; - strncpy((char *) data->apn, apn, sizeof(data->apn)); + memcpy(data->apn, apn, sizeof(data->apn)); return 0; } @@ -56,9 +56,9 @@ int ipc_gprs_pdp_context_request_set_setup( data->magic1[2] = 0x13; data->magic2 = 0x01; - strncpy((char *) data->username, username, + memcpy(data->username, username, sizeof(data->username)); - strncpy((char *) data->password, password, + memcpy(data->password, password, sizeof(data->password)); } diff --git a/samsung-ipc/ipc_strings.c b/samsung-ipc/ipc_strings.c new file mode 100644 index 0000000..e7e6e8b --- /dev/null +++ b/samsung-ipc/ipc_strings.c @@ -0,0 +1,409 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2010-2011 Joerie de Gram <j.de.gram@gmail.com> + * Copyright (C) 2013-2014 Paul Kocialkowski <contact@paulk.fr> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> + +#include <samsung-ipc.h> + +const char *ipc_request_type_string(unsigned char type) +{ + static char type_string[5] = { 0 }; + + switch (type) { + case IPC_TYPE_EXEC: + return "IPC_TYPE_EXEC"; + case IPC_TYPE_GET: + return "IPC_TYPE_GET"; + case IPC_TYPE_SET: + return "IPC_TYPE_SET"; + case IPC_TYPE_CFRM: + return "IPC_TYPE_CFRM"; + case IPC_TYPE_EVENT: + return "IPC_TYPE_EVENT"; + default: + snprintf((char *) &type_string, sizeof(type_string), "0x%02x", + type); + return type_string; + } +} + +const char *ipc_response_type_string(unsigned char type) +{ + static char type_string[5] = { 0 }; + + switch (type) { + case IPC_TYPE_INDI: + return "IPC_TYPE_INDI"; + case IPC_TYPE_RESP: + return "IPC_TYPE_RESP"; + case IPC_TYPE_NOTI: + return "IPC_TYPE_NOTI"; + default: + snprintf((char *) &type_string, sizeof(type_string), "0x%02x", + type); + return type_string; + } +} + +const char *ipc_command_string(unsigned short command) +{ + static char command_string[7] = { 0 }; + + switch (command) { + case IPC_PWR_PHONE_PWR_UP: + return "IPC_PWR_PHONE_PWR_UP"; + case IPC_PWR_PHONE_PWR_OFF: + return "IPC_PWR_PHONE_PWR_OFF"; + case IPC_PWR_PHONE_RESET: + return "IPC_PWR_PHONE_RESET"; + case IPC_PWR_BATT_STATUS: + return "IPC_PWR_BATT_STATUS"; + case IPC_PWR_BATT_TYPE: + return "IPC_PWR_BATT_TYPE"; + case IPC_PWR_BATT_COMP: + return "IPC_PWR_BATT_COMP"; + case IPC_PWR_PHONE_STATE: + return "IPC_PWR_PHONE_STATE"; + case IPC_CALL_OUTGOING: + return "IPC_CALL_OUTGOING"; + case IPC_CALL_INCOMING: + return "IPC_CALL_INCOMING"; + case IPC_CALL_RELEASE: + return "IPC_CALL_RELEASE"; + case IPC_CALL_ANSWER: + return "IPC_CALL_ANSWER"; + case IPC_CALL_STATUS: + return "IPC_CALL_STATUS"; + case IPC_CALL_LIST: + return "IPC_CALL_LIST"; + case IPC_CALL_BURST_DTMF: + return "IPC_CALL_BURST_DTMF"; + case IPC_CALL_CONT_DTMF: + return "IPC_CALL_CONT_DTMF"; + case IPC_CALL_WAITING: + return "IPC_CALL_WAITING"; + case IPC_CALL_LINE_ID: + return "IPC_CALL_LINE_ID"; + case IPC_SMS_SEND_MSG: + return "IPC_SMS_SEND_MSG"; + case IPC_SMS_INCOMING_MSG: + return "IPC_SMS_INCOMING_MSG"; + case IPC_SMS_READ_MSG: + return "IPC_SMS_READ_MSG"; + case IPC_SMS_SAVE_MSG: + return "IPC_SMS_SAVE_MSG"; + case IPC_SMS_DEL_MSG: + return "IPC_SMS_DEL_MSG"; + case IPC_SMS_DELIVER_REPORT: + return "IPC_SMS_DELIVER_REPORT"; + case IPC_SMS_DEVICE_READY: + return "IPC_SMS_DEVICE_READY"; + case IPC_SMS_SEL_MEM: + return "IPC_SMS_SEL_MEM"; + case IPC_SMS_STORED_MSG_COUNT: + return "IPC_SMS_STORED_MSG_COUNT"; + case IPC_SMS_SVC_CENTER_ADDR: + return "IPC_SMS_SVC_CENTER_ADDR"; + case IPC_SMS_SVC_OPTION: + return "IPC_SMS_SVC_OPTION"; + case IPC_SMS_MEM_STATUS: + return "IPC_SMS_MEM_STATUS"; + case IPC_SMS_CBS_MSG: + return "IPC_SMS_CBS_MSG"; + case IPC_SMS_CBS_CFG: + return "IPC_SMS_CBS_CFG"; + case IPC_SMS_STORED_MSG_STATUS: + return "IPC_SMS_STORED_MSG_STATUS"; + case IPC_SMS_PARAM_COUNT: + return "IPC_SMS_PARAM_COUNT"; + case IPC_SMS_PARAM: + return "IPC_SMS_PARAM"; + case IPC_SEC_PIN_STATUS: + return "IPC_SEC_PIN_STATUS"; + case IPC_SEC_PHONE_LOCK: + return "IPC_SEC_PHONE_LOCK"; + case IPC_SEC_CHANGE_LOCKING_PW: + return "IPC_SEC_CHANGE_LOCKING_PW"; + case IPC_SEC_SIM_LANG: + return "IPC_SEC_SIM_LANG"; + case IPC_SEC_RSIM_ACCESS: + return "IPC_SEC_RSIM_ACCESS"; + case IPC_SEC_GSIM_ACCESS: + return "IPC_SEC_GSIM_ACCESS"; + case IPC_SEC_SIM_ICC_TYPE: + return "IPC_SEC_SIM_ICC_TYPE"; + case IPC_SEC_LOCK_INFORMATION: + return "IPC_SEC_LOCK_INFORMATION"; + case IPC_SEC_IMS_AUTH: + return "IPC_SEC_IMS_AUTH"; + case IPC_PB_ACCESS: + return "IPC_PB_ACCESS"; + case IPC_PB_STORAGE: + return "IPC_PB_STORAGE"; + case IPC_PB_STORAGE_LIST: + return "IPC_PB_STORAGE_LIST"; + case IPC_PB_ENTRY_INFO: + return "IPC_PB_ENTRY_INFO"; + case IPC_PB_3GPB_CAPA: + return "IPC_PB_3GPB_CAPA"; + case IPC_DISP_ICON_INFO: + return "IPC_DISP_ICON_INFO"; + case IPC_DISP_HOMEZONE_INFO: + return "IPC_DISP_HOMEZONE_INFO"; + case IPC_DISP_RSSI_INFO: + return "IPC_DISP_RSSI_INFO"; + case IPC_NET_PREF_PLMN: + return "IPC_NET_PREF_PLMN"; + case IPC_NET_PLMN_SEL: + return "IPC_NET_PLMN_SEL"; + case IPC_NET_SERVING_NETWORK: + return "IPC_NET_SERVING_NETWORK"; + case IPC_NET_PLMN_LIST: + return "IPC_NET_PLMN_LIST"; + case IPC_NET_REGIST: + return "IPC_NET_REGIST"; + case IPC_NET_SUBSCRIBER_NUM: + return "IPC_NET_SUBSCRIBER_NUM"; + case IPC_NET_BAND_SEL: + return "IPC_NET_BAND_SEL"; + case IPC_NET_SERVICE_DOMAIN_CONFIG: + return "IPC_NET_SERVICE_DOMAIN_CONFIG"; + case IPC_NET_POWERON_ATTACH: + return "IPC_NET_POWERON_ATTACH"; + case IPC_NET_MODE_SEL: + return "IPC_NET_MODE_SEL"; + case IPC_NET_ACQ_ORDER: + return "IPC_NET_ACQ_ORDER"; + case IPC_NET_IDENTITY: + return "IPC_NET_IDENTITY"; + case IPC_NET_PREFERRED_NETWORK_INFO: + return "IPC_NET_PREFERRED_NETWORK_INFO"; + case IPC_SND_SPKR_VOLUME_CTRL: + return "IPC_SND_SPKR_VOLUME_CTRL"; + case IPC_SND_MIC_MUTE_CTRL: + return "IPC_SND_MIC_MUTE_CTRL"; + case IPC_SND_AUDIO_PATH_CTRL: + return "IPC_SND_AUDIO_PATH_CTRL"; + case IPC_SND_AUDIO_SOURCE_CTRL: + return "IPC_SND_AUDIO_SOURCE_CTRL"; + case IPC_SND_LOOPBACK_CTRL: + return "IPC_SND_LOOPBACK_CTRL"; + case IPC_SND_VOICE_RECORDING_CTRL: + return "IPC_SND_VOICE_RECORDING_CTRL"; + case IPC_SND_VIDEO_CALL_CTRL: + return "IPC_SND_VIDEO_CALL_CTRL"; + case IPC_SND_RINGBACK_TONE_CTRL: + return "IPC_SND_RINGBACK_TONE_CTRL"; + case IPC_SND_CLOCK_CTRL: + return "IPC_SND_CLOCK_CTRL"; + case IPC_SND_WB_AMR_STATUS: + return "IPC_SND_WB_AMR_STATUS"; + case IPC_MISC_ME_VERSION: + return "IPC_MISC_ME_VERSION"; + case IPC_MISC_ME_IMSI: + return "IPC_MISC_ME_IMSI"; + case IPC_MISC_ME_SN: + return "IPC_MISC_ME_SN"; + case IPC_MISC_TIME_INFO: + return "IPC_MISC_TIME_INFO"; + case IPC_MISC_DEBUG_LEVEL: + return "IPC_MISC_DEBUG_LEVEL"; + case IPC_SVC_ENTER: + return "IPC_SVC_ENTER"; + case IPC_SVC_END: + return "IPC_SVC_END"; + case IPC_SVC_PRO_KEYCODE: + return "IPC_SVC_PRO_KEYCODE"; + case IPC_SVC_SCREEN_CFG: + return "IPC_SVC_SCREEN_CFG"; + case IPC_SVC_DISPLAY_SCREEN: + return "IPC_SVC_DISPLAY_SCREEN"; + case IPC_SVC_CHANGE_SVC_MODE: + return "IPC_SVC_CHANGE_SVC_MODE"; + case IPC_SVC_DEVICE_TEST: + return "IPC_SVC_DEVICE_TEST"; + case IPC_SVC_DEBUG_DUMP: + return "IPC_SVC_DEBUG_DUMP"; + case IPC_SVC_DEBUG_STRING: + return "IPC_SVC_DEBUG_STRING"; + case IPC_SS_WAITING: + return "IPC_SS_WAITING"; + case IPC_SS_CLI: + return "IPC_SS_CLI"; + case IPC_SS_BARRING: + return "IPC_SS_BARRING"; + case IPC_SS_BARRING_PW: + return "IPC_SS_BARRING_PW"; + case IPC_SS_FORWARDING: + return "IPC_SS_FORWARDING"; + case IPC_SS_INFO: + return "IPC_SS_INFO"; + case IPC_SS_MANAGE_CALL: + return "IPC_SS_MANAGE_CALL"; + case IPC_SS_USSD: + return "IPC_SS_USSD"; + case IPC_SS_AOC: + return "IPC_SS_AOC"; + case IPC_SS_RELEASE_COMPLETE: + return "IPC_SS_RELEASE_COMPLETE"; + case IPC_GPRS_DEFINE_PDP_CONTEXT: + return "IPC_GPRS_DEFINE_PDP_CONTEXT"; + case IPC_GPRS_QOS: + return "IPC_GPRS_QOS"; + case IPC_GPRS_PS: + return "IPC_GPRS_PS"; + case IPC_GPRS_PDP_CONTEXT: + return "IPC_GPRS_PDP_CONTEXT"; + case IPC_GPRS_ENTER_DATA: + return "IPC_GPRS_ENTER_DATA"; + case IPC_GPRS_SHOW_PDP_ADDR: + return "IPC_GPRS_SHOW_PDP_ADDR"; + case IPC_GPRS_MS_CLASS: + return "IPC_GPRS_MS_CLASS"; + case IPC_GPRS_3G_QUAL_SRVC_PROFILE: + return "IPC_GPRS_3G_QUAL_SRVC_PROFILE"; + case IPC_GPRS_IP_CONFIGURATION: + return "IPC_GPRS_IP_CONFIGURATION"; + case IPC_GPRS_DEFINE_SEC_PDP_CONTEXT: + return "IPC_GPRS_DEFINE_SEC_PDP_CONTEXT"; + case IPC_GPRS_TFT: + return "IPC_GPRS_TFT"; + case IPC_GPRS_HSDPA_STATUS: + return "IPC_GPRS_HSDPA_STATUS"; + case IPC_GPRS_CURRENT_SESSION_DATA_COUNTER: + return "IPC_GPRS_CURRENT_SESSION_DATA_COUNTER"; + case IPC_GPRS_DATA_DORMANT: + return "IPC_GPRS_DATA_DORMANT"; + case IPC_GPRS_PIN_CTRL: + return "IPC_GPRS_PIN_CTRL"; + case IPC_GPRS_CALL_STATUS: + return "IPC_GPRS_CALL_STATUS"; + case IPC_GPRS_PORT_LIST: + return "IPC_GPRS_PORT_LIST"; + case IPC_SAT_PROFILE_DOWNLOAD: + return "IPC_SAT_PROFILE_DOWNLOAD"; + case IPC_SAT_ENVELOPE_CMD: + return "IPC_SAT_ENVELOPE_CMD"; + case IPC_SAT_PROACTIVE_CMD: + return "IPC_SAT_PROACTIVE_CMD"; + case IPC_SAT_TERMINATE_USAT_SESSION: + return "IPC_SAT_TERMINATE_USAT_SESSION"; + case IPC_SAT_EVENT_DOWNLOAD: + return "IPC_SAT_EVENT_DOWNLOAD"; + case IPC_SAT_PROVIDE_LOCAL_INFO: + return "IPC_SAT_PROVIDE_LOCAL_INFO"; + case IPC_SAT_POLLING: + return "IPC_SAT_POLLING"; + case IPC_SAT_REFRESH: + return "IPC_SAT_REFRESH"; + case IPC_SAT_SETUP_EVENT_LIST: + return "IPC_SAT_SETUP_EVENT_LIST"; + case IPC_SAT_CALL_CONTROL_RESULT: + return "IPC_SAT_CALL_CONTROL_RESULT"; + case IPC_SAT_IMAGE_CLUT: + return "IPC_SAT_IMAGE_CLUT"; + case IPC_SAT_SETUP_CALL_PROCESSING: + return "IPC_SAT_SETUP_CALL_PROCESSING"; + case IPC_IMEI_START: + return "IPC_IMEI_START"; + case IPC_IMEI_CHECK_DEVICE_INFO: + return "IPC_IMEI_CHECK_DEVICE_INFO"; + case IPC_RFS_NV_READ_ITEM: + return "IPC_RFS_NV_READ_ITEM"; + case IPC_RFS_NV_WRITE_ITEM: + return "IPC_RFS_NV_WRITE_ITEM"; + case IPC_GEN_PHONE_RES: + return "IPC_GEN_PHONE_RES"; + default: + snprintf((char *) &command_string, sizeof(command_string), + "0x%04x", command); + return command_string; + } +} + +const char *ipc_group_string(unsigned char group) +{ + static char group_string[5] = { 0 }; + + switch (group) { + case IPC_GROUP_PWR: + return "IPC_GROUP_PWR"; + case IPC_GROUP_CALL: + return "IPC_GROUP_CALL"; + case IPC_GROUP_SMS: + return "IPC_GROUP_SMS"; + case IPC_GROUP_SEC: + return "IPC_GROUP_SEC"; + case IPC_GROUP_PB: + return "IPC_GROUP_PB"; + case IPC_GROUP_DISP: + return "IPC_GROUP_DISP"; + case IPC_GROUP_NET: + return "IPC_GROUP_NET"; + case IPC_GROUP_SND: + return "IPC_GROUP_SND"; + case IPC_GROUP_MISC: + return "IPC_GROUP_MISC"; + case IPC_GROUP_SVC: + return "IPC_GROUP_SVC"; + case IPC_GROUP_SS: + return "IPC_GROUP_SS"; + case IPC_GROUP_GPRS: + return "IPC_GROUP_GPRS"; + case IPC_GROUP_SAT: + return "IPC_GROUP_SAT"; + case IPC_GROUP_CFG: + return "IPC_GROUP_CFG"; + case IPC_GROUP_IMEI: + return "IPC_GROUP_IMEI"; + case IPC_GROUP_GPS: + return "IPC_GROUP_GPS"; + case IPC_GROUP_SAP: + return "IPC_GROUP_SAP"; + case IPC_GROUP_RFS: + return "IPC_GROUP_RFS"; + case IPC_GROUP_GEN: + return "IPC_GROUP_GEN"; + default: + snprintf((char *) &group_string, sizeof(group_string), "0x%02x", + (unsigned int)group); + return group_string; + } +} + +const char *ipc_client_type_string(unsigned char client_type) +{ + static char client_type_string[5] = { 0 }; + + switch (client_type) { + case IPC_CLIENT_TYPE_FMT: + return "FMT"; + case IPC_CLIENT_TYPE_RFS: + return "RFS"; + case IPC_CLIENT_TYPE_DUMMY: + return "DUMMY"; + default: + snprintf((char *) &client_type_string, + sizeof(client_type_string), + "0x%02x", + client_type); + return client_type_string; + } +} diff --git a/samsung-ipc/ipc_utils.c b/samsung-ipc/ipc_utils.c index d8b69b7..3518475 100644 --- a/samsung-ipc/ipc_utils.c +++ b/samsung-ipc/ipc_utils.c @@ -41,322 +41,6 @@ int ipc_seq_valid(unsigned char seq) return 1; } -const char *ipc_request_type_string(unsigned char type) -{ - static char type_string[5] = { 0 }; - - switch (type) { - case IPC_TYPE_EXEC: - return "IPC_TYPE_EXEC"; - case IPC_TYPE_GET: - return "IPC_TYPE_GET"; - case IPC_TYPE_SET: - return "IPC_TYPE_SET"; - case IPC_TYPE_CFRM: - return "IPC_TYPE_CFRM"; - case IPC_TYPE_EVENT: - return "IPC_TYPE_EVENT"; - default: - snprintf((char *) &type_string, sizeof(type_string), "0x%02x", - type); - return type_string; - } -} - -const char *ipc_response_type_string(unsigned char type) -{ - static char type_string[5] = { 0 }; - - switch (type) { - case IPC_TYPE_INDI: - return "IPC_TYPE_INDI"; - case IPC_TYPE_RESP: - return "IPC_TYPE_RESP"; - case IPC_TYPE_NOTI: - return "IPC_TYPE_NOTI"; - default: - snprintf((char *) &type_string, sizeof(type_string), "0x%02x", - type); - return type_string; - } -} - -const char *ipc_command_string(unsigned short command) -{ - static char command_string[7] = { 0 }; - - switch (command) { - case IPC_PWR_PHONE_PWR_UP: - return "IPC_PWR_PHONE_PWR_UP"; - case IPC_PWR_PHONE_PWR_OFF: - return "IPC_PWR_PHONE_PWR_OFF"; - case IPC_PWR_PHONE_RESET: - return "IPC_PWR_PHONE_RESET"; - case IPC_PWR_BATT_STATUS: - return "IPC_PWR_BATT_STATUS"; - case IPC_PWR_BATT_TYPE: - return "IPC_PWR_BATT_TYPE"; - case IPC_PWR_BATT_COMP: - return "IPC_PWR_BATT_COMP"; - case IPC_PWR_PHONE_STATE: - return "IPC_PWR_PHONE_STATE"; - case IPC_CALL_OUTGOING: - return "IPC_CALL_OUTGOING"; - case IPC_CALL_INCOMING: - return "IPC_CALL_INCOMING"; - case IPC_CALL_RELEASE: - return "IPC_CALL_RELEASE"; - case IPC_CALL_ANSWER: - return "IPC_CALL_ANSWER"; - case IPC_CALL_STATUS: - return "IPC_CALL_STATUS"; - case IPC_CALL_LIST: - return "IPC_CALL_LIST"; - case IPC_CALL_BURST_DTMF: - return "IPC_CALL_BURST_DTMF"; - case IPC_CALL_CONT_DTMF: - return "IPC_CALL_CONT_DTMF"; - case IPC_CALL_WAITING: - return "IPC_CALL_WAITING"; - case IPC_CALL_LINE_ID: - return "IPC_CALL_LINE_ID"; - case IPC_SMS_SEND_MSG: - return "IPC_SMS_SEND_MSG"; - case IPC_SMS_INCOMING_MSG: - return "IPC_SMS_INCOMING_MSG"; - case IPC_SMS_READ_MSG: - return "IPC_SMS_READ_MSG"; - case IPC_SMS_SAVE_MSG: - return "IPC_SMS_SAVE_MSG"; - case IPC_SMS_DEL_MSG: - return "IPC_SMS_DEL_MSG"; - case IPC_SMS_DELIVER_REPORT: - return "IPC_SMS_DELIVER_REPORT"; - case IPC_SMS_DEVICE_READY: - return "IPC_SMS_DEVICE_READY"; - case IPC_SMS_SEL_MEM: - return "IPC_SMS_SEL_MEM"; - case IPC_SMS_STORED_MSG_COUNT: - return "IPC_SMS_STORED_MSG_COUNT"; - case IPC_SMS_SVC_CENTER_ADDR: - return "IPC_SMS_SVC_CENTER_ADDR"; - case IPC_SMS_SVC_OPTION: - return "IPC_SMS_SVC_OPTION"; - case IPC_SMS_MEM_STATUS: - return "IPC_SMS_MEM_STATUS"; - case IPC_SMS_CBS_MSG: - return "IPC_SMS_CBS_MSG"; - case IPC_SMS_CBS_CFG: - return "IPC_SMS_CBS_CFG"; - case IPC_SMS_STORED_MSG_STATUS: - return "IPC_SMS_STORED_MSG_STATUS"; - case IPC_SMS_PARAM_COUNT: - return "IPC_SMS_PARAM_COUNT"; - case IPC_SMS_PARAM: - return "IPC_SMS_PARAM"; - case IPC_SEC_PIN_STATUS: - return "IPC_SEC_PIN_STATUS"; - case IPC_SEC_PHONE_LOCK: - return "IPC_SEC_PHONE_LOCK"; - case IPC_SEC_CHANGE_LOCKING_PW: - return "IPC_SEC_CHANGE_LOCKING_PW"; - case IPC_SEC_SIM_LANG: - return "IPC_SEC_SIM_LANG"; - case IPC_SEC_RSIM_ACCESS: - return "IPC_SEC_RSIM_ACCESS"; - case IPC_SEC_GSIM_ACCESS: - return "IPC_SEC_GSIM_ACCESS"; - case IPC_SEC_SIM_ICC_TYPE: - return "IPC_SEC_SIM_ICC_TYPE"; - case IPC_SEC_LOCK_INFORMATION: - return "IPC_SEC_LOCK_INFORMATION"; - case IPC_SEC_IMS_AUTH: - return "IPC_SEC_IMS_AUTH"; - case IPC_PB_ACCESS: - return "IPC_PB_ACCESS"; - case IPC_PB_STORAGE: - return "IPC_PB_STORAGE"; - case IPC_PB_STORAGE_LIST: - return "IPC_PB_STORAGE_LIST"; - case IPC_PB_ENTRY_INFO: - return "IPC_PB_ENTRY_INFO"; - case IPC_PB_3GPB_CAPA: - return "IPC_PB_3GPB_CAPA"; - case IPC_DISP_ICON_INFO: - return "IPC_DISP_ICON_INFO"; - case IPC_DISP_HOMEZONE_INFO: - return "IPC_DISP_HOMEZONE_INFO"; - case IPC_DISP_RSSI_INFO: - return "IPC_DISP_RSSI_INFO"; - case IPC_NET_PREF_PLMN: - return "IPC_NET_PREF_PLMN"; - case IPC_NET_PLMN_SEL: - return "IPC_NET_PLMN_SEL"; - case IPC_NET_SERVING_NETWORK: - return "IPC_NET_SERVING_NETWORK"; - case IPC_NET_PLMN_LIST: - return "IPC_NET_PLMN_LIST"; - case IPC_NET_REGIST: - return "IPC_NET_REGIST"; - case IPC_NET_SUBSCRIBER_NUM: - return "IPC_NET_SUBSCRIBER_NUM"; - case IPC_NET_BAND_SEL: - return "IPC_NET_BAND_SEL"; - case IPC_NET_SERVICE_DOMAIN_CONFIG: - return "IPC_NET_SERVICE_DOMAIN_CONFIG"; - case IPC_NET_POWERON_ATTACH: - return "IPC_NET_POWERON_ATTACH"; - case IPC_NET_MODE_SEL: - return "IPC_NET_MODE_SEL"; - case IPC_NET_ACQ_ORDER: - return "IPC_NET_ACQ_ORDER"; - case IPC_NET_IDENTITY: - return "IPC_NET_IDENTITY"; - case IPC_NET_PREFERRED_NETWORK_INFO: - return "IPC_NET_PREFERRED_NETWORK_INFO"; - case IPC_SND_SPKR_VOLUME_CTRL: - return "IPC_SND_SPKR_VOLUME_CTRL"; - case IPC_SND_MIC_MUTE_CTRL: - return "IPC_SND_MIC_MUTE_CTRL"; - case IPC_SND_AUDIO_PATH_CTRL: - return "IPC_SND_AUDIO_PATH_CTRL"; - case IPC_SND_AUDIO_SOURCE_CTRL: - return "IPC_SND_AUDIO_SOURCE_CTRL"; - case IPC_SND_LOOPBACK_CTRL: - return "IPC_SND_LOOPBACK_CTRL"; - case IPC_SND_VOICE_RECORDING_CTRL: - return "IPC_SND_VOICE_RECORDING_CTRL"; - case IPC_SND_VIDEO_CALL_CTRL: - return "IPC_SND_VIDEO_CALL_CTRL"; - case IPC_SND_RINGBACK_TONE_CTRL: - return "IPC_SND_RINGBACK_TONE_CTRL"; - case IPC_SND_CLOCK_CTRL: - return "IPC_SND_CLOCK_CTRL"; - case IPC_SND_WB_AMR_STATUS: - return "IPC_SND_WB_AMR_STATUS"; - case IPC_MISC_ME_VERSION: - return "IPC_MISC_ME_VERSION"; - case IPC_MISC_ME_IMSI: - return "IPC_MISC_ME_IMSI"; - case IPC_MISC_ME_SN: - return "IPC_MISC_ME_SN"; - case IPC_MISC_TIME_INFO: - return "IPC_MISC_TIME_INFO"; - case IPC_MISC_DEBUG_LEVEL: - return "IPC_MISC_DEBUG_LEVEL"; - case IPC_SVC_ENTER: - return "IPC_SVC_ENTER"; - case IPC_SVC_END: - return "IPC_SVC_END"; - case IPC_SVC_PRO_KEYCODE: - return "IPC_SVC_PRO_KEYCODE"; - case IPC_SVC_SCREEN_CFG: - return "IPC_SVC_SCREEN_CFG"; - case IPC_SVC_DISPLAY_SCREEN: - return "IPC_SVC_DISPLAY_SCREEN"; - case IPC_SVC_CHANGE_SVC_MODE: - return "IPC_SVC_CHANGE_SVC_MODE"; - case IPC_SVC_DEVICE_TEST: - return "IPC_SVC_DEVICE_TEST"; - case IPC_SVC_DEBUG_DUMP: - return "IPC_SVC_DEBUG_DUMP"; - case IPC_SVC_DEBUG_STRING: - return "IPC_SVC_DEBUG_STRING"; - case IPC_SS_WAITING: - return "IPC_SS_WAITING"; - case IPC_SS_CLI: - return "IPC_SS_CLI"; - case IPC_SS_BARRING: - return "IPC_SS_BARRING"; - case IPC_SS_BARRING_PW: - return "IPC_SS_BARRING_PW"; - case IPC_SS_FORWARDING: - return "IPC_SS_FORWARDING"; - case IPC_SS_INFO: - return "IPC_SS_INFO"; - case IPC_SS_MANAGE_CALL: - return "IPC_SS_MANAGE_CALL"; - case IPC_SS_USSD: - return "IPC_SS_USSD"; - case IPC_SS_AOC: - return "IPC_SS_AOC"; - case IPC_SS_RELEASE_COMPLETE: - return "IPC_SS_RELEASE_COMPLETE"; - case IPC_GPRS_DEFINE_PDP_CONTEXT: - return "IPC_GPRS_DEFINE_PDP_CONTEXT"; - case IPC_GPRS_QOS: - return "IPC_GPRS_QOS"; - case IPC_GPRS_PS: - return "IPC_GPRS_PS"; - case IPC_GPRS_PDP_CONTEXT: - return "IPC_GPRS_PDP_CONTEXT"; - case IPC_GPRS_ENTER_DATA: - return "IPC_GPRS_ENTER_DATA"; - case IPC_GPRS_SHOW_PDP_ADDR: - return "IPC_GPRS_SHOW_PDP_ADDR"; - case IPC_GPRS_MS_CLASS: - return "IPC_GPRS_MS_CLASS"; - case IPC_GPRS_3G_QUAL_SRVC_PROFILE: - return "IPC_GPRS_3G_QUAL_SRVC_PROFILE"; - case IPC_GPRS_IP_CONFIGURATION: - return "IPC_GPRS_IP_CONFIGURATION"; - case IPC_GPRS_DEFINE_SEC_PDP_CONTEXT: - return "IPC_GPRS_DEFINE_SEC_PDP_CONTEXT"; - case IPC_GPRS_TFT: - return "IPC_GPRS_TFT"; - case IPC_GPRS_HSDPA_STATUS: - return "IPC_GPRS_HSDPA_STATUS"; - case IPC_GPRS_CURRENT_SESSION_DATA_COUNTER: - return "IPC_GPRS_CURRENT_SESSION_DATA_COUNTER"; - case IPC_GPRS_DATA_DORMANT: - return "IPC_GPRS_DATA_DORMANT"; - case IPC_GPRS_PIN_CTRL: - return "IPC_GPRS_PIN_CTRL"; - case IPC_GPRS_CALL_STATUS: - return "IPC_GPRS_CALL_STATUS"; - case IPC_GPRS_PORT_LIST: - return "IPC_GPRS_PORT_LIST"; - case IPC_SAT_PROFILE_DOWNLOAD: - return "IPC_SAT_PROFILE_DOWNLOAD"; - case IPC_SAT_ENVELOPE_CMD: - return "IPC_SAT_ENVELOPE_CMD"; - case IPC_SAT_PROACTIVE_CMD: - return "IPC_SAT_PROACTIVE_CMD"; - case IPC_SAT_TERMINATE_USAT_SESSION: - return "IPC_SAT_TERMINATE_USAT_SESSION"; - case IPC_SAT_EVENT_DOWNLOAD: - return "IPC_SAT_EVENT_DOWNLOAD"; - case IPC_SAT_PROVIDE_LOCAL_INFO: - return "IPC_SAT_PROVIDE_LOCAL_INFO"; - case IPC_SAT_POLLING: - return "IPC_SAT_POLLING"; - case IPC_SAT_REFRESH: - return "IPC_SAT_REFRESH"; - case IPC_SAT_SETUP_EVENT_LIST: - return "IPC_SAT_SETUP_EVENT_LIST"; - case IPC_SAT_CALL_CONTROL_RESULT: - return "IPC_SAT_CALL_CONTROL_RESULT"; - case IPC_SAT_IMAGE_CLUT: - return "IPC_SAT_IMAGE_CLUT"; - case IPC_SAT_SETUP_CALL_PROCESSING: - return "IPC_SAT_SETUP_CALL_PROCESSING"; - case IPC_IMEI_START: - return "IPC_IMEI_START"; - case IPC_IMEI_CHECK_DEVICE_INFO: - return "IPC_IMEI_CHECK_DEVICE_INFO"; - case IPC_RFS_NV_READ_ITEM: - return "IPC_RFS_NV_READ_ITEM"; - case IPC_RFS_NV_WRITE_ITEM: - return "IPC_RFS_NV_WRITE_ITEM"; - case IPC_GEN_PHONE_RES: - return "IPC_GEN_PHONE_RES"; - default: - snprintf((char *) &command_string, sizeof(command_string), - "0x%04x", command); - return command_string; - } -} - int ipc_data_dump(struct ipc_client *client, const void *data, size_t size) { unsigned int cols = 8; diff --git a/samsung-ipc/modems/Makefile.am b/samsung-ipc/modems/Makefile.am new file mode 100644 index 0000000..f59258a --- /dev/null +++ b/samsung-ipc/modems/Makefile.am @@ -0,0 +1,15 @@ +libsamsung_ipc_la_SOURCES += \ + modems/xmm616/xmm616.c \ + modems/xmm616/xmm616.h \ + modems/xmm626/xmm626.c \ + modems/xmm626/xmm626.h \ + modems/xmm626/xmm626_hsic.c \ + modems/xmm626/xmm626_hsic.h \ + modems/xmm626/xmm626_kernel_smdk4412.c \ + modems/xmm626/xmm626_kernel_smdk4412.h \ + modems/xmm626/xmm626_mipi.c \ + modems/xmm626/xmm626_mipi.h \ + modems/xmm626/xmm626_modem_if.h \ + modems/xmm626/xmm626_modem_link_device_hsic.h \ + modems/xmm626/xmm626_modem_prj.h \ + $(NULL) diff --git a/samsung-ipc/modems/xmm626/xmm626_kernel_smdk4412.c b/samsung-ipc/modems/xmm626/xmm626_kernel_smdk4412.c index c6b1578..cdac3e4 100644 --- a/samsung-ipc/modems/xmm626/xmm626_kernel_smdk4412.c +++ b/samsung-ipc/modems/xmm626/xmm626_kernel_smdk4412.c @@ -19,11 +19,13 @@ */ #define _GNU_SOURCE -#include <stdlib.h> + +#include <fcntl.h> #include <stdio.h> -#include <unistd.h> +#include <stdlib.h> #include <string.h> -#include <fcntl.h> +#include <unistd.h> + #include <sys/ioctl.h> #include <sys/select.h> @@ -582,11 +584,17 @@ char *xmm626_kernel_smdk4412_gprs_get_iface( __attribute__((unused)) struct ipc_client *client, unsigned int cid) { char *iface = NULL; + int rc; if (cid > XMM626_SEC_MODEM_GPRS_IFACE_COUNT) return NULL; - asprintf(&iface, "%s%d", XMM626_SEC_MODEM_GPRS_IFACE_PREFIX, cid - 1); + rc = asprintf(&iface, "%s%d", + XMM626_SEC_MODEM_GPRS_IFACE_PREFIX, cid - 1); + if (rc == -1) { + ipc_client_log(client, "%s: asprintf failed", __func__); + return NULL; + } return iface; } diff --git a/samsung-ipc/partitions/Makefile.am b/samsung-ipc/partitions/Makefile.am new file mode 100644 index 0000000..5248869 --- /dev/null +++ b/samsung-ipc/partitions/Makefile.am @@ -0,0 +1,6 @@ +libsamsung_ipc_la_SOURCES += \ + partitions/android/android.c \ + partitions/android/android.h \ + partitions/toc/toc.c \ + partitions/toc/toc.h \ + $(NULL) diff --git a/samsung-ipc/partitions/android/android.c b/samsung-ipc/partitions/android/android.c new file mode 100644 index 0000000..9d2322f --- /dev/null +++ b/samsung-ipc/partitions/android/android.c @@ -0,0 +1,118 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2013-2014 Paul Kocialkowski <contact@paulk.fr> + * Copyright (C) 2017 Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> + * Copyright (C) 2020 Tony Garnock-Jones <tonyg@leastfixedpoint.com> + * Copyright (C) 2021 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/stat.h> +#include <sys/types.h> + +#include <samsung-ipc.h> + +static char const * const partitions_dirnames[] = { + "/dev/disk/by-partlabel/", /* GNU/Linux */ + "/dev/block/by-name/", /* Android */ + NULL +}; + +int open_android_modem_partition(struct ipc_client *client, + char const * const *path_names) +{ + int i; + + for (i = 0; path_names[i] != NULL; i++) { + char const * const path = path_names[i]; + int fd; + + ipc_client_log(client, "%s: Trying to open %s", + __func__, path); + + fd = open(path, O_RDONLY); + if (fd == -1) { + if (errno == ENOENT) + continue; + /* Normally errno should be passed to the caller here */ + return -1; + } + return fd; + } + + errno = ENOENT; + return -1; +} + +int open_android_modem_partition_by_name(struct ipc_client *client, + const char *name, char **out_path) +{ + int i; + int rc; + + for (i = 0; partitions_dirnames[i] != NULL; i++) { + char *path = NULL; + int fd; + size_t len; + + len = strlen(partitions_dirnames[i]) + strlen(name) + 1; + path = calloc(1, len); + if (path == NULL) { + rc = errno; + ipc_client_log(client, + "%s: calloc failed with error %d: %s", + __func__, rc, strerror(rc)); + return -errno; + } + + strcpy(path, partitions_dirnames[i]); + strcat(path, name); + + ipc_client_log(client, "%s: Trying to open %s", + __func__, path); + + fd = open(path, O_RDONLY); + if (fd == -1) { + rc = -errno; + if (out_path) + *out_path = path; + else + free(path); + + if (rc == -ENOENT) + continue; + + errno = -rc; + return -1; + } + + if (out_path) + *out_path = path; + else + free(path); + + return fd; + } + + errno = ENOENT; + return -1; +} diff --git a/samsung-ipc/partitions/android/android.h b/samsung-ipc/partitions/android/android.h new file mode 100644 index 0000000..e2f23f7 --- /dev/null +++ b/samsung-ipc/partitions/android/android.h @@ -0,0 +1,29 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2013-2014 Paul Kocialkowski <contact@paulk.fr> + * Copyright (C) 2017 Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> + * Copyright (C) 2020 Tony Garnock-Jones <tonyg@leastfixedpoint.com> + * Copyright (C) 2021 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __ANDROID_PARTITIONS_H__ +#define __ANDROID_PARTITIONS_H__ +int open_android_modem_partition(struct ipc_client *client, + char const * const *path_names); +int open_android_modem_partition_by_name(struct ipc_client *client, + const char *name, char **out_path); +#endif /* __ANDROID_PARTITIONS_H__ */ diff --git a/samsung-ipc/partitions/toc/toc.c b/samsung-ipc/partitions/toc/toc.c new file mode 100644 index 0000000..ec62ea9 --- /dev/null +++ b/samsung-ipc/partitions/toc/toc.c @@ -0,0 +1,44 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2020 Tony Garnock-Jones <tonyg@leastfixedpoint.com> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stddef.h> +#include <string.h> + +#include "toc.h" + +struct firmware_toc_entry const *find_toc_entry( + char const *name, + struct firmware_toc_entry const *toc) +{ + unsigned int index; + + /* We don't know all the details of the TOC format yet; for now, we + * assume two things: + * 1. reading 512 bytes of TOC is enough, and + * 2. the first entry with an empty name field ends the list. + */ + for (index = 0; index < N_TOC_ENTRIES; index++) { + if (toc[index].name[0] == '\0') + break; + if (strncmp(toc[index].name, name, + sizeof(toc[index].name)) == 0) + return &toc[index]; + } + return NULL; +} diff --git a/samsung-ipc/partitions/toc/toc.h b/samsung-ipc/partitions/toc/toc.h new file mode 100644 index 0000000..416a45f --- /dev/null +++ b/samsung-ipc/partitions/toc/toc.h @@ -0,0 +1,41 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2020 Tony Garnock-Jones <tonyg@leastfixedpoint.com> + * Copyright (C) 2021 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __TOC_PARTITION_TABLE_H__ +#define __TOC_PARTITION_TABLE_H__ + +#include <stdint.h> + +struct __attribute__((__packed__)) firmware_toc_entry { + char name[12]; + uint32_t offset; /* offset within firmware file/partition */ + uint32_t loadaddr; /* target memory address for this blob */ + uint32_t size; /* size of this blob in bytes */ + uint32_t crc; + uint32_t entryid; +}; + +#define N_TOC_ENTRIES (512 / sizeof(struct firmware_toc_entry)) + +struct firmware_toc_entry const *find_toc_entry( + char const *name, + struct firmware_toc_entry const *toc); + +#endif /* __TOC_PARTITION_TABLE_H__ */ diff --git a/samsung-ipc/rfs.c b/samsung-ipc/rfs.c index 114df99..c9cee1b 100644 --- a/samsung-ipc/rfs.c +++ b/samsung-ipc/rfs.c @@ -32,20 +32,40 @@ #include "ipc.h" +#define MD5_DIGEST_LENGTH_ASCII (2 * MD5_DIGEST_LENGTH) + char *ipc_nv_data_md5_calculate(struct ipc_client *client, const char *path, const char *secret, size_t size, size_t chunk_size) { + struct stat st; void *data = NULL; char *md5_string = NULL; unsigned char md5_hash[MD5_DIGEST_LENGTH] = { 0 }; MD5_CTX ctx; + int rc; if (secret == NULL) { ipc_client_log(client, "%s: Failed: secret is NULL", __func__); return NULL; } + rc = stat(path, &st); + if (rc == -1) { + rc = errno; + ipc_client_log(client, "%s: stat failed with error %d", + __func__, rc, strerror(rc)); + return NULL; + } + + if ((unsigned long)st.st_size != size) { + ipc_client_log(client, + "%s: Checking %s size failed: " + "requested size: %d, file size: %d\n", + __func__, path, size, st.st_size); + return NULL; + } + data = file_data_read(client, path, size, chunk_size, 0); if (data == NULL) { ipc_client_log(client, "%s failed: data is NULL", __func__); @@ -112,7 +132,7 @@ int ipc_nv_data_md5_path_check(struct ipc_client *client) return -1; } - if (st.st_size < 2 * sizeof(char) * MD5_DIGEST_LENGTH) { + if (st.st_size != MD5_DIGEST_LENGTH_ASCII) { ipc_client_log(client, "Checking nv_data md5 size failed"); return -1; } @@ -173,7 +193,7 @@ int ipc_nv_data_backup_md5_path_check(struct ipc_client *client) return -1; } - if (st.st_size < 2 * sizeof(char) * MD5_DIGEST_LENGTH) { + if (st.st_size != MD5_DIGEST_LENGTH_ASCII) { ipc_client_log(client, "Checking nv_data backup md5 size failed"); return -1; @@ -493,7 +513,7 @@ int ipc_nv_data_restore(struct ipc_client *client) free(data); data = NULL; - length = 2 * sizeof(char) * MD5_DIGEST_LENGTH; + length = MD5_DIGEST_LENGTH_ASCII; data = file_data_read(client, backup_md5_path, length, length, 0); if (data == NULL) { diff --git a/samsung-ipc/tests/Makefile.am b/samsung-ipc/tests/Makefile.am new file mode 100644 index 0000000..ad4280e --- /dev/null +++ b/samsung-ipc/tests/Makefile.am @@ -0,0 +1,39 @@ +NULL = +EXTRA_DIST = $(NULL) + +AM_CFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/samsung-ipc \ + $(OPENSSL_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = $(ALL_STATIC_LDFLAGS) + +if WANT_STRICT_CFLAGS +AM_CFLAGS += $(STRICT_CFLAGS) +endif + +if WANT_DEBUG +AM_CFLAGS += -DDEBUG +endif + +bin_PROGRAMS = libsamsung-ipc-test + +libsamsung_ipc_test_SOURCES = \ + main.c \ + partitions/android.c \ + partitions/android.h \ + $(NULL) + +libsamsung_ipc_test_LDADD = $(top_builddir)/samsung-ipc/libsamsung-ipc.la + +# TODO: Find a way to make test more modular and represent each run of +# libsamsung-ipc-test in TEST while having it implemented in a single +# python file +if WANT_VALGRIND_CHECKING +AM_TESTS_ENVIRONMENT = VALGRIND='$(VALGRIND)'; export VALGRIND; +endif +PY_LOG_COMPILER = $(PYTHON3) +TEST_EXTENSIONS = .py +TESTS = libsamsung-ipc-test.py +EXTRA_DIST += $(TESTS) diff --git a/samsung-ipc/tests/libsamsung-ipc-test.py b/samsung-ipc/tests/libsamsung-ipc-test.py new file mode 100755 index 0000000..5435013 --- /dev/null +++ b/samsung-ipc/tests/libsamsung-ipc-test.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# +# This file is part of libsamsung-ipc. +# +# Copyright (C) 2020 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> +# +# libsamsung-ipc is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# libsamsung-ipc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + +import os +import re +import sys +import sh + +class libsamsung_ipc_test(object): + def __init__(self): + srcdir = os.environ.get('srcdir', None) + + command_path = '' + if srcdir: + command_path = '.' + os.sep + 'libsamsung-ipc-test' + # Enable to run tests without automake + else: + command_path = os.path.dirname(sys.argv[0]) \ + + os.sep \ + + 'libsamsung-ipc-test' + + if 'VALGRIND' in os.environ: + self.run = sh.Command(os.environ['VALGRIND']) + self.run = self.run.bake('-v', + '--log-file=valgrind.%p.log', + '--leak-check=full', + command_path) + else: + self.run = sh.Command(command_path) + + def run_all_tests(self): + output = str(self.run('list-tests')).split(os.linesep) + # Remove the last line break from the output + output.remove('') + + # Also Remove the first line from the output: We have an output like + # that: + # Available tests: + # [list of tests] + output.pop(0) + + for test_name in output: + self.run('test', test_name.replace(' ', '')) + +def main(): + tests = libsamsung_ipc_test() + tests.run_all_tests() + +if __name__ == '__main__': + main() diff --git a/samsung-ipc/tests/main.c b/samsung-ipc/tests/main.c new file mode 100644 index 0000000..ffb855d --- /dev/null +++ b/samsung-ipc/tests/main.c @@ -0,0 +1,157 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2016 Paul Kocialkowsk <contact@paulk.fr> + * Copyright (C) 2021 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#include <samsung-ipc.h> + +/* libsamsung-ipc internal headers */ +#include <ipc.h> +#include "partitions/android.h" + +struct test { + char *name; + int (*func)(struct ipc_client *client); +}; + +static struct test tests[] = { + { + "open_android_modem_partition", + test_open_android_modem_partition + }, +}; + +static void usage(const char *progname) +{ + printf("Usage: %s list-tests\n", progname); + printf("Usage: %s test <test>\n", progname); +} + +static void list_tests(void) +{ + unsigned int i; + + printf("Available tests:\n"); + + for (i = 0; i < sizeof(tests) / sizeof(struct test); i++) + printf(" %s\n", tests[i].name); +} + +static struct test *get_test(char *name) +{ + unsigned int i; + + for (i = 0; i < sizeof(tests) / sizeof(struct test); i++) { + if (!strcmp(tests[i].name, name)) + return &(tests[i]); + } + + return NULL; +} + +static void log_callback(__attribute__((unused)) void *data, + __attribute__((unused)) const char *message) +{ + /* TODO: add better logging mechanism in libsamsung-ipc with + * tags and log levels which would enable to filter the + * messages from different provenance (this file, the code + * under test, other parts of libsamsung-ipc, etc). + */ +#ifdef DEBUG + char *buffer; + size_t length; + int i; + + if (message == NULL) + return; + + buffer = strdup(message); + length = strlen(message); + + for (i = length; i > 0; i--) { + if (buffer[i] == '\n') + buffer[i] = '\0'; + else if (buffer[i] != '\0') + break; + } + + printf("[ipc] %s\n", buffer); + + free(buffer); +#endif +} + +int main(int argc, char *argv[]) +{ + struct ipc_client *client = NULL; + const char *progname = "libsamsung-ipc-test"; + char *given_test_name; + struct test *test; + int rc = 0; + + if (argc == 2 && !strcmp("list-tests", argv[1])) { + list_tests(); + return 0; + } else if (argc == 3 && !strcmp("test", argv[1])) { + given_test_name = argv[2]; + } else { + usage(progname); + return EX_USAGE; + } + + test = get_test(given_test_name); + if (test == NULL) { + printf("Unknown test %s\n", given_test_name); + return EX_USAGE; + } + + client = ipc_client_create(IPC_CLIENT_TYPE_DUMMY); + if (client == NULL) { + printf("[ !! ] Creating client failed\n"); + goto error; + } + + rc = ipc_client_log_callback_register(client, log_callback, NULL); + if (rc < 0) { + printf("[ !! ] Registering log callback failed: error %d\n", + rc); + goto error; + } + + rc = test->func(client); + if (rc == 0) { + printf("[ OK ] %s succedded\n", test->name); + } else { + printf("[ !! ] %s failed\n", test->name); + goto error; + } + + return 0; + +error: + if (client != NULL) + ipc_client_destroy(client); + + return EX_SOFTWARE; +} diff --git a/samsung-ipc/tests/partitions/android.c b/samsung-ipc/tests/partitions/android.c new file mode 100644 index 0000000..7b067e9 --- /dev/null +++ b/samsung-ipc/tests/partitions/android.c @@ -0,0 +1,212 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2021 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/stat.h> +#include <sys/types.h> + +#include <samsung-ipc.h> + +#include <partitions/android/android.h> +#include "android.h" + +static char const * const dummy_modem_image_paths[] = { + /* We can't use mktemp here since everything is const + * echo libsamsung-ipc | sha1sum | cut -c-20 + * gives 55f4731d2e11e85bd889 + */ + "/tmp/libsamsung-ipc.55f4731d2e11e85bd889/modem.img", + NULL +}; + +int delete_dummy_modem_image(struct ipc_client *client, + __attribute__((unused)) const char * const path) +{ + int rc; + char *endp; + char *dir; + + rc = unlink(path); + if (rc == -1) { + rc = errno; + if (rc != ENOENT) { + ipc_client_log(client, + "%s: unlink %s failed with error %d: %s", + __func__, path, rc, strerror(rc)); + errno = rc; + return -1; + } + } + + endp = strrchr(path, '/'); + + dir = malloc(endp - path + 1); + if (dir == NULL) { + rc = errno; + ipc_client_log(client, + "%s: calloc failed with error %d: %s", + __func__, rc, strerror(rc)); + errno = rc; + return -1; + + } + + memcpy(dir, path, endp - path); + dir[endp - path] = '\0'; + + rc = rmdir(dir); + if (rc == -1) { + rc = errno; + if (rc != ENOENT) { + ipc_client_log(client, + "%s: rmdir %s failed with error %d: %s", + __func__, dir, rc, strerror(rc)); + + free(dir); + errno = rc; + return -1; + } + } + + free(dir); + return 0; +} + + +int create_dummy_modem_image(struct ipc_client *client, + __attribute__((unused)) const char * const path) +{ + int fd; + int rc; + + rc = mkdir("/tmp/", 0755); + if (rc == -1) { + rc = errno; + if (rc != EEXIST) { + ipc_client_log(client, + "%s: mkdir %s failed with error %d: %s", + __func__, "/tmp/", rc, strerror(rc)); + return -1; + } + } + + rc = mkdir("/tmp/libsamsung-ipc.55f4731d2e11e85bd889/", 0755); + if (rc == -1) { + rc = errno; + if (rc != EEXIST) { + ipc_client_log(client, + "%s: mkdir %s failed with error %d: %s", + __func__, + "/tmp/libsamsung-ipc.55f4731d2e11e85bd889/", + rc, strerror(rc)); + return -1; + } + } + + fd = open("/tmp/libsamsung-ipc.55f4731d2e11e85bd889/modem.img", + O_CREAT, 0755); + if (fd == -1) { + rc = errno; + ipc_client_log(client, + "%s: open %s failed with error %d: %s", + __func__, + "/tmp/libsamsung-ipc.55f4731d2e11e85bd889/modem.img", + rc, strerror(rc)); + goto error; + } + + rc = close(fd); + if (rc == -1) { + rc = errno; + ipc_client_log(client, + "%s: close %s failed with error %d: %s", + __func__, + "/tmp/libsamsung-ipc.55f4731d2e11e85bd889/modem.img", + rc, strerror(rc)); + goto error; + } + + return 0; + +error: + rc = delete_dummy_modem_image( + client, "/tmp/libsamsung-ipc.55f4731d2e11e85bd889/modem.img"); + if (rc == -1) + ipc_client_log(client, + "%s: delete_dummy_modem_image %s failed with error -1", + __func__); + return -1; +} + +int test_open_android_modem_partition(struct ipc_client *client) +{ + int i; + int rc; + + for (i = 0; dummy_modem_image_paths[i] != NULL; i++) { + rc = create_dummy_modem_image(client, + dummy_modem_image_paths[i]); + if (rc == -1) { + ipc_client_log( + client, + "%s: create_dummy_modem_image(client, %s)" + " failed\n", + __func__, dummy_modem_image_paths[i]); + return -1; + } + } + + rc = open_android_modem_partition(client, dummy_modem_image_paths); + if (rc == -1) { + rc = errno; + ipc_client_log(client, "%s: open_android_modem_partition" + " failed with errror %d: %s\n", + __func__, rc, strerror(rc)); + errno = rc; + return -1; + } + + rc = close(rc); + if (rc == -1) { + rc = errno; + ipc_client_log(client, + "%s: close() failed with errror %d: %s\n", + __func__, rc, strerror(rc)); + return -1; + } + + rc = delete_dummy_modem_image( + client, "/tmp/libsamsung-ipc.55f4731d2e11e85bd889/modem.img"); + if (rc == -1) { + rc = errno; + ipc_client_log(client, + "%s: delete_dummy_modem_image() failed with errror %d: %s\n", + __func__, rc, strerror(rc)); + return -1; + } + + return 0; +} diff --git a/samsung-ipc/tests/partitions/android.h b/samsung-ipc/tests/partitions/android.h new file mode 100644 index 0000000..a386bb7 --- /dev/null +++ b/samsung-ipc/tests/partitions/android.h @@ -0,0 +1,25 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2021 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __TESTS_PARTITIONS_ANDROID_H__ +#define __TESTS_PARTITIONS_ANDROID_H__ + +int test_open_android_modem_partition(struct ipc_client *client); + +#endif /* __TESTS_PARTITIONS_ANDROID_H__ */ diff --git a/samsung-ipc/utils.c b/samsung-ipc/utils.c index 0e6bc2f..18f5d2b 100644 --- a/samsung-ipc/utils.c +++ b/samsung-ipc/utils.c @@ -38,6 +38,49 @@ #include <samsung-ipc.h> +ssize_t data_read(__attribute__((unused)) struct ipc_client *client, int fd, + void *buf, size_t count) +{ + /* From read(2): "According to POSIX.1, if count is greater than + * SSIZE_MAX, the result is implementation-defined" + */ + ssize_t remaining = (ssize_t)count; + + while (remaining > 0) { + ssize_t rc; + + rc = read(fd, buf, count); + if (rc == -1) + /* errno is passed to the caller */ + return rc; + remaining -= rc; + } + + return count; +} + +ssize_t data_write(__attribute__((unused)) struct ipc_client *client, int fd, + const void *buf, size_t count) +{ + /* From write(2): "According to POSIX.1, if count is greater than + * SSIZE_MAX, the result is implementation-defined" + */ + ssize_t remaining = (ssize_t)count; + + while (remaining > 0) { + ssize_t rc; + + rc = write(fd, buf, count); + if (rc == -1) + /* errno is passed to the caller */ + return rc; + remaining -= rc; + } + + return count; + +} + void *file_data_read(struct ipc_client *client, const char *path, size_t size, size_t chunk_size, unsigned int offset) { @@ -93,8 +136,14 @@ void *file_data_read(struct ipc_client *client, const char *path, size_t size, rc = read(fd, p, size - count > chunk_size ? chunk_size : size - count); - if (rc <= 0) { - ipc_client_log(client, "%s: Error: rc < 0", __func__); + if (rc == -1) { + rc = errno; + ipc_client_log(client, "%s: read error: %d: %s", + __func__, rc, strerror(rc)); + goto error; + } else if (rc == 0) { + ipc_client_log(client, "%s: read error: end of file", + __func__); goto error; } @@ -145,6 +194,7 @@ int file_data_write(struct ipc_client *client, const char *path, ipc_client_log(client, "%s failed: chunk_size > size", __func__); } + errno = EINVAL; return -1; } @@ -158,6 +208,7 @@ int file_data_write(struct ipc_client *client, const char *path, seek = lseek(fd, (off_t) offset, SEEK_SET); if (seek < (off_t) offset) { + rc = errno; ipc_client_log(client, "%s failed: seek < (off_t) offset", __func__); goto error; @@ -180,17 +231,58 @@ int file_data_write(struct ipc_client *client, const char *path, count += rc; } - rc = 0; - goto complete; + if (fd >= 0) + close(fd); + + return 0; error: - rc = -1; + if (fd >= 0) + close(fd); -complete: + errno = rc; + return -1; +} + +off_t file_data_size(struct ipc_client *client, const char *path) +{ + int fd = -1; + off_t rc = 0; + int err = 0; + + if (path == NULL) { + ipc_client_log(client, "%s: Failed: path is NULL", + __func__); + err = ENOENT; + goto error; + } + + fd = open(path, O_RDONLY); + if (fd < 0) { + err = errno; + ipc_client_log(client, "%s: open %s failed with error %d: %s", + __func__, path, err, strerror(err)); + goto error; + } + + rc = lseek(fd, 0, SEEK_END); + if (rc == -1) { + err = errno; + ipc_client_log(client, "%s: seek %s failed with error %d: %s", + __func__, path, err, strerror(err)); + goto error; + } + +error: if (fd >= 0) close(fd); - return rc; + if (err) { + errno = err; + return -1; + } else { + return rc; + } } int network_iface_up(const char *iface, int domain, int type) @@ -203,7 +295,7 @@ int network_iface_up(const char *iface, int domain, int type) return -1; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, iface, IFNAMSIZ); + memcpy(ifr.ifr_name, iface, IFNAMSIZ); fd = socket(domain, type, 0); if (fd < 0) @@ -242,7 +334,7 @@ int network_iface_down(const char *iface, int domain, int type) return -1; memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, iface, IFNAMSIZ); + memcpy(ifr.ifr_name, iface, IFNAMSIZ); fd = socket(domain, type, 0); if (fd < 0) diff --git a/scripts/PKGBUILD b/scripts/PKGBUILD new file mode 100644 index 0000000..155a765 --- /dev/null +++ b/scripts/PKGBUILD @@ -0,0 +1,55 @@ +# Copyright (C) 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +pkgname=libsamsung-ipc-git +pkgver=0.3.1.r703.7c5e96a +pkgrel=1 +pkgdesc="samsung-ipc modem protocol implementation." +arch=('armv7h' 'i686' 'x86_64') +license=('GPL2') +makedepends=('autoconf' + 'automake' + 'coreutils' + 'ddrescue' + 'gawk' + 'grep' + 'libtool' + 'pkg-config' + 'python' + 'python-sh' + 'sed' + 'valgrind') +depends=('openssl') + +pkgver() { + _base_version="0.3.1" + _base_revision="5cb3e7cb99701cae738693f2d6a6797a4d2df679" + + if [ -d ../../.git ] ; then + printf "%s.r%s.%s" \ + "${_base_version}" \ + "$(git -C ../../ rev-list --count HEAD)" \ + "$(git -C ../../ rev-parse --short HEAD)" + else + date "+%Y%m%d" + fi +} + +build() { + ../../autogen.sh + ../../configure \ + --prefix=/usr \ + --enable-debug --enable-valgrind-tests --enable-strict-cflags + make +} + +check() { + make check +} + +package(){ + make DESTDIR="$pkgdir" install +} diff --git a/scripts/guix.scm b/scripts/guix.scm new file mode 100644 index 0000000..6343208 --- /dev/null +++ b/scripts/guix.scm @@ -0,0 +1,336 @@ +;;; Copyright © 2020 Denis Carikli <GNUtoo@cyberdimension.org> +;;; +;;; This file is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; This file is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. +;;; The guix.scm file is a convention: A file named guix.scm is +;;; found in several project source code, either in the top +;;; directory or in sub-directories like contrib for instance. + +;;; The guix.scm files typically contain package definitions that +;;; are not meant to be used as regular packages but are meant for +;;; testing or developing on the given project. +;;; +;;; For instance here it is used to build libsamsung-ipc in various +;;; configurations. Instead distributions would typically be interested +;;; in only one of the configurations. +;;; +;;; It also copies the sources that are present in the directory that +;;; contains the guix.scm, and builds them. Distributions would instead +;;; be interested in retrieving the source from git or releases tarballs. +;;; +;;; Once you have Guix installed, to build libsamsung-ipc with the +;;; guix.scm file, you can use the 'guix build --file=scripts/guix.scm' +;;; command, however as explained above, keep in mind that it will copy +;;; all the files in the same directory than guix.scm, so you might want +;;; to make sure that the sources are clean (with 'make distclean') if +;;; you already built libsamsung-ipc in that directory. +;;; +;;; While this file could also serve as a basis to make a libsamsung-ipc +;;; package in Guix, it is probably a good idea to wait until the API +;;; and ABI changes are complete, ie when the ipc_client has been passed +;;; to all the exported functions, and make a new release when that is +;;; done. + +(use-modules (ice-9 popen) + (ice-9 rdelim) + (ice-9 regex) + (ice-9 textual-ports) + (sxml ssax input-parse) + ((guix licenses) + #:prefix license:) + (guix build-system android-ndk) + (guix build-system gnu) + (guix gexp) + (guix git-download) + (guix packages) + (guix profiles) + (guix transformations) + (gnu packages) + (gnu packages android) + (gnu packages autotools) + (gnu packages base) + (gnu packages curl) + (gnu packages disk) + (gnu packages linux) + (gnu packages llvm) + (gnu packages pkg-config) + (gnu packages python) + (gnu packages python-xyz) + (gnu packages tls) + (gnu packages valgrind)) + +;; We need a more recent version of android-make-stub as it now +;; support passing LOCAL_MODULE= to make to build specific local +;; modules. This is needed because android-make-stub doesn't handle +;; dependencies, so we need to make sure that libsamsung-ipc is built +;; first. In addition we need a fix to make the applications that +;; depend on libsamsung-ipc find libsamsung-ipc's includes directory. +(define with-fixed-android-make-stub + (options->transformation '((with-commit . "android-make-stub=4bc0e068c32fe525741da00af064ddc079de7e4b")))) + +(define (parse-android.mk port modules-list) + (let* ((line (read-line port))) + (if (not (eof-object? line)) + (let* ((line-parts (string-split line #\space))) + (if (string=? (car line-parts) "LOCAL_MODULE") + (if (not (string=? (list-ref line-parts 2) "libsamsung-ipc")) + (if (string=? modules-list "") + (parse-android.mk port + (list-ref line-parts 2)) + (parse-android.mk port + (string-append modules-list " " + (list-ref line-parts 2)))) + (parse-android.mk port modules-list)) + (parse-android.mk port modules-list))) modules-list))) + +(define android-local-modules-list + (let* ((port (open-input-file "./Android.mk")) + (local-modules (parse-android.mk port ""))) + (close-input-port port) + (string-split local-modules #\space))) + +(define %common-strict-cflags + (let* ((port (open-input-pipe "./strict-cflags.sh")) + (str (read-line port))) + (close-pipe port) + (string-append "CFLAGS=" str))) + +(define %clang-strict-cflags + (string-append "-Werror=non-virtual-dtor")) + +(define %commit + (let* ((port (open-input-pipe + "git --no-pager log --oneline HEAD -1 --format='%H'")) + (str (read-line port))) + (close-pipe port) str)) + +(define %local-source + (local-file (dirname (dirname (current-filename))) + #:recursive? #t)) + +(define android-make + #~(lambda (target android-local-modules) + (lambda* (#:key inputs make-flags native-inputs outputs + #:allow-other-keys) + (substitute* "Android.mk" + (("BUILD_SHARED_LIBRARY") + "BUILD_HOST_SHARED_LIBRARY") + (("BUILD_EXECUTABLE") + "BUILD_HOST_EXECUTABLE")) + ((assoc-ref %standard-phases target) + #:inputs inputs + #:outputs outputs + #:make-flags (append make-flags + '("LDFLAGS=-lssl -lcrypto" + "LOCAL_MODULE=libsamsung-ipc" + "SO=libsamsung-ipc.so"))) + (map-in-order (lambda (local-module) + ((assoc-ref %standard-phases target) + #:inputs inputs + #:outputs outputs + #:make-flags (append make-flags + (list (string-append + "LDFLAGS=-Wl,-rpath=" + #$output + "/lib -lssl -lcrypto -L .") + (string-append + "LOCAL_MODULE=" + local-module))))) + android-local-modules)))) + +(define android-phases + #~(modify-phases %standard-phases + (delete 'bootstrap) + (replace 'build + (#$android-make + 'build + '#$android-local-modules-list)) + (replace 'install + (#$android-make + 'install + '#$android-local-modules-list)))) + +(define-public libsamsung-ipc + (package + (name "libsamsung-ipc") + (version (git-version "0.0" "HEAD" %commit)) + (source %local-source) + (build-system gnu-build-system) + (native-inputs (list autoconf + automake + ddrescue + `(,(canonical-package glibc) "debug") + libtool + pkg-config + python + python-sh + valgrind)) + (inputs (list curl libressl)) + (arguments + (list #:phases #~(modify-phases %standard-phases + (add-before 'build 'fix-valgrind + (lambda _ + (substitute* (find-files "." ".*\\.py$") + (("'--leak-check=full',") + (string-append + "'--leak-check=full', '--extra-debuginfo-path=" + (ungexp (this-package-native-input "glibc") + "debug") "/lib/debug',")))))) + #:configure-flags #~(list "--enable-debug" + "--enable-valgrind-tests"))) + (synopsis + "libsamsung-ipc is a free software implementation of the Samsung IPC modem protocol") + (description + "libsamsung-ipc is a free software implementation of the Samsung IPC modem protocol, +found in many Samsung smartphones and tablets.") + (home-page "https://www.replicant.us") + (license license:gpl2+))) + +(define is-file-static + #~(lambda (readelf path) + ;; str is also eof if the file doesn't exist + (if (not (file-exists? tool)) #f + (let* ((port (open-input-pipe (string-append readelf " --dyn-syms " + path))) + (str (read-line port))) + (close-pipe port) + (eof-object? str))))) + +(define-public libsamsung-ipc-static + (package + (inherit libsamsung-ipc) + (name "libsamsung-ipc-static") + (native-inputs (list autoconf + automake + binutils + ddrescue + libtool + pkg-config + python + python-sh)) + ;; The libressl package contains .a in + ;; /gnu/store/[...]-libressl-[...]/lib/*.a + (inputs (list curl libressl)) + (arguments + (list #:modules '((ice-9 popen) + (ice-9 rdelim) + (guix build utils) + (guix build gnu-build-system)) + #:tests? #f + #:phases #~(modify-phases %standard-phases + ;; https-send-sms depends on curl and Guix doesn't have + ;; a static libcurl. + (add-after 'unpack 'remove-https-send-sms + (lambda _ + (substitute* "tools/Makefile.am" + (("https-send-sms \\\\") + "\\")))) + (add-after 'compress-documentation 'check-static-files + (lambda _ + (display "Checking static files:\n") + (map-in-order (lambda (tool) + (if (#$is-file-static + (string-append #$(this-package-native-input + "binutils") + "/bin/readelf") tool) + (display (string-append + "[ OK ] " + (basename tool) ": " + tool "\n")) + ((lambda _ + (display (string-append + "[ !! ] " + (basename tool) + ": " tool "\n")) + (#f))))) + (list + "samsung-ipc/tests/libsamsung-ipc-test" + "tools/ipc-test" + "tools/nv_data-imei" + "tools/nv_data-md5" + "tools/ipc-modem/ipc-modem"))))) + #:configure-flags #~(list "--enable-debug" "--enable-static=yes" + "--enable-shared=no" + "--enable-static-progs"))))) + +(define-public libsamsung-ipc-gcc-android + (package + (inherit libsamsung-ipc) + (name "libsamsung-ipc-gcc-android") + (build-system android-ndk-build-system) + (inputs (list android-libutils curl libressl)) + (native-inputs '()) + (arguments + (list #:make-flags #~(list #$%common-strict-cflags + #$%clang-strict-cflags) + #:phases android-phases)))) + +(define-public libsamsung-ipc-gcc-autotools + (package + (inherit libsamsung-ipc) + (name "libsamsung-ipc-gcc-autotools") + (arguments + (list #:configure-flags #~(list "--enable-debug") + #:make-flags #~(list #$%common-strict-cflags))))) + +(define-public libsamsung-ipc-clang-autotools + (package + (inherit libsamsung-ipc) + (name "libsamsung-ipc-clang-autotools") + (native-inputs (list autoconf + automake + ddrescue + libtool + pkg-config + python + python-sh + clang)) + (arguments + (list #:configure-flags #~(list "--enable-debug") + #:make-flags #~(list #$%common-strict-cflags + #$%clang-strict-cflags))))) + +(define-public libsamsung-ipc-clang-android + (package + (inherit libsamsung-ipc) + (name "libsamsung-ipc-clang-android") + (build-system android-ndk-build-system) + (inputs (list android-libutils clang curl libressl)) + (native-inputs '()) + (arguments + (list #:make-flags #~(list #$%common-strict-cflags + #$%clang-strict-cflags) + #:phases android-phases)))) + +;; Combinaisons: +;; +--------------------------------+----------+----------+--------------+--------------+ +;; | Package name | Compiler | Compiler | Build system | Comments | +;; | | | flags | | | +;; +--------------------------------+----------+----------+--------------+--------------+ +;; | libsamsung-ipc | GCC | none | autotools | Base package | +;; +--------------------------------+----------+----------+--------------+--------------+ +;; | libsamsung-ipc-gcc-android | GCC | strict | Android.mk | | +;; +--------------------------------+----------+----------+--------------+--------------+ +;; | libsamsung-ipc-gcc-autotools | GCC | strict | autotools | | +;; +--------------------------------+----------+----------+--------------+--------------+ +;; | libsamsung-ipc-clang-android | clang | strict | Android.mk | | +;; +--------------------------------+----------+----------+--------------+--------------+ +;; | libsamsung-ipc-clang-autotools | clang | strict | autotools | | +;; +--------------------------------+----------+----------+--------------+--------------+ + +(list libsamsung-ipc + libsamsung-ipc-static + (with-fixed-android-make-stub libsamsung-ipc-clang-android) + (with-fixed-android-make-stub libsamsung-ipc-gcc-android) + libsamsung-ipc-clang-autotools + libsamsung-ipc-gcc-autotools) diff --git a/scripts/manifest.scm b/scripts/manifest.scm new file mode 100644 index 0000000..dfc82c7 --- /dev/null +++ b/scripts/manifest.scm @@ -0,0 +1,59 @@ +;;; Copyright © 2021 Denis Carikli <GNUtoo@cyberdimension.org> +;;; +;;; This file is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; This file is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. +;;; The guix.scm file is a convention: A file named guix.scm is +;;; found in several project source code, either in the top +;;; directory or in sub-directories like contrib for instance. + +;;; This file can be used to setup a development environment for Guix +;;; users with the following command: +;;; guix shell --pure --container -f scripts/manifest.scm + +(use-modules + (gnu packages autotools) + (gnu packages base) + (gnu packages code) + (gnu packages commencement) + (gnu packages compression) + (gnu packages curl) + (gnu packages disk) + (gnu packages gawk) + (gnu packages pkg-config) + (gnu packages python) + (gnu packages python-xyz) + (gnu packages tls)) + +(list + autoconf + automake + bzip2 + coreutils + curl + ddrescue + diffutils + findutils + gawk + gzip + gcc-toolchain + grep + lcov + libtool + gnu-make + openssl + pkg-config + python + python-sh + sed + tar + xz) diff --git a/scripts/rebase-build-check-android.sh b/scripts/rebase-build-check-android.sh new file mode 100755 index 0000000..e43e3c6 --- /dev/null +++ b/scripts/rebase-build-check-android.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# -*- coding: utf-8 -*- +# Copyright (C) 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +set -e + +usage() +{ + printf "Usage:\n" + printf "%s build-from <base-commit-ref> " "$0" + printf "# Build all commits since <base-commit-ref>\n" "%0" + printf "%s continue " "$0" + printf "# Continue rebasing and building commits\n" "%0" + exit 64; # EX_USAGE from sysexits.h +} + +print_header() +{ + set +e + message="$1" + + len=$(expr $(echo -n ${message} | wc -c) + 2) + + nr_chars=$(expr $(expr 80 - ${len}) / 2) + + i="${nr_chars}" + + while [ ${i} -gt 0 ] ; do + echo -n "#" + i="$(expr ${i} - 1)" + done + + echo -n " ${message} " + + i="${nr_chars}" + + while [ ${i} -gt 0 ] ; do + echo -n "#" + i="$(expr ${i} - 1)" + done + echo + set -e +} + +build_and_git_rebase_continue() +{ + total_commits="$1" + + total_targets="$(./vendor/replicant/build.sh targets | wc -l)" + parallel_tasks=$(echo "$(grep 'processor' /proc/cpuinfo | wc -l ) + 1" | bc) + + local_modules="$(grep \ + "^LOCAL_MODULE := " \ + hardware/replicant/libsamsung-ipc/Android.mk | \ + awk '{print $3}' | sort -u)" + + source build/envsetup.sh + + # Currently all the Replicant versions supported by libsamsung-ipc + # have an aosp_arm-eng target. Using aosp_arm-eng also ensures + # that TARGET_DEVICE isn't set by the device repository. This also + # leaves us more freedom in libsamsung-ipc as we could for + # instance decide to not build unused code based on TARGET_DEVICE. + lunch aosp_arm-eng + + commit_nr=1 + while true ; do + print_header "[ commit ${commit_nr} of ${total_commits} ]" + make "-j${parallel_tasks}" ${local_modules} + git -C hardware/replicant/libsamsung-ipc rebase --continue || break + commit_nr="$(expr ${commit_nr} + 1)" + done +} + +if [ $# -eq 2 ] && [ "$1" == "build-from" ] ; then + base_commit="$2" + nr_commits="$(git -C hardware/replicant/libsamsung-ipc log --oneline \ + ${base_commit}~1..HEAD --oneline | wc -l)" + GIT_EDITOR="sed 's#^pick #edit #g' -i " \ + git -C hardware/replicant/libsamsung-ipc \ + rebase -i "${base_commit}" + + build_and_git_rebase_continue "${nr_commits}" +elif [ $# -eq 1 ] && [ "$1" == "continue" ] ; then + nr_commits="$(git -C hardware/replicant/libsamsung-ipc \ + log --oneline REBASE_HEAD~1..ORIG_HEAD --oneline | wc -l)" + build_and_git_rebase_continue "${nr_commits}" +else + usage +fi diff --git a/scripts/rebase-build-check-gnu-linux.sh b/scripts/rebase-build-check-gnu-linux.sh new file mode 100755 index 0000000..a909290 --- /dev/null +++ b/scripts/rebase-build-check-gnu-linux.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# -*- coding: utf-8 -*- +# Copyright (C) 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +die() +{ + echo "$@" + exit 1; +} + +usage() +{ + printf "Usage:\n" + printf "%s build-from <base-commit-ref> " "$0" + printf "# Build all commits since <base-commit-ref>\n" "%0" + printf "%s continue " "$0" + printf "# Continue rebasing and building commits\n" "%0" + exit 64; # EX_USAGE from sysexits.h +} + +print_header() +{ + message="$1" + + len=$(expr $(echo -n ${message} | wc -c) + 2) + + nr_chars=$(expr $(expr 80 - ${len}) / 2) + + i="${nr_chars}" + while [ ${i} -gt 0 ] ; do + echo -n "#" + i="$(expr ${i} - 1)" + done + + echo -n " ${message} " + + i="${nr_chars}" + while [ ${i} -gt 0 ] ; do + echo -n "#" + i="$(expr ${i} - 1)" + done + echo +} + +build_and_git_rebase_continue() +{ + commits="$1" + commit=1 + + while true ; do + print_header "[ ${commit} of ${commits} ]" + guix build --file=scripts/guix.scm || die "guix build failed" + commit="$(expr ${commit} + 1)" + git rebase --continue || break + done +} + +if [ $# -eq 2 ] && [ "$1" == "build-from" ] ; then + base_commit="$2" + nr_commits="$(git log --oneline ${base_commit}~1..HEAD --oneline | wc -l)" + GIT_EDITOR="sed 's#^pick #edit #g' -i " git rebase -i "${base_commit}" + build_and_git_rebase_continue "${nr_commits}" +elif [ $# -eq 1 ] && [ "$1" == "continue" ] ; then + nr_commits="$(git log --oneline REBASE_HEAD~1..ORIG_HEAD --oneline | wc -l)" + build_and_git_rebase_continue "${nr_commits}" +else + usage +fi diff --git a/strict-cflags.sh b/strict-cflags.sh new file mode 100755 index 0000000..55380f3 --- /dev/null +++ b/strict-cflags.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# +# This file is part of libsamsung-ipc. +# +# Copyright (C) 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> +# +# libsamsung-ipc is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# libsamsung-ipc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + +strict_cflags="\ + -W \ + -Wall \ + -Werror \ + -Werror=address \ + -Werror=return-type \ + -Werror=sequence-point \ + -Winit-self \ + -Wno-unused \ + -Wpedantic \ + -Wpointer-arith \ + -Wunused \ + -Wunused-function \ +" + +echo ${strict_cflags} diff --git a/tools/Makefile.am b/tools/Makefile.am index 9336a64..024df50 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,23 +1,44 @@ NULL = +EXTRA_DIST = $(NULL) AM_CFLAGS = \ -I$(top_srcdir)/include \ + $(LIBCURL_CFLAGS) \ $(NULL) +AM_LDFLAGS = $(ALL_STATIC_LDFLAGS) + +if WANT_STRICT_CFLAGS +AM_CFLAGS += $(STRICT_CFLAGS) +endif + bin_PROGRAMS = \ - ipc-modem \ + https-send-sms \ ipc-test \ + nv_data-imei \ nv_data-md5 \ $(NULL) -ipc_modem_SOURCES = ipc-modem.c -ipc_modem_LDADD = $(top_builddir)/samsung-ipc/libsamsung-ipc.la -ipc_modem_LDFLAGS = +# TODO: Find a way to make test more modular and represent each run of the +# nv_data-imei in TEST while having it implemented in a single python file +if WANT_VALGRIND_CHECKING +AM_TESTS_ENVIRONMENT = VALGRIND='$(VALGRIND)'; export VALGRIND; +endif +PY_LOG_COMPILER = $(PYTHON3) +TEST_EXTENSIONS = .py +TESTS = \ + tests/nv_data-imei.py \ + tests/nv_data-md5.py +EXTRA_DIST += $(TESTS) + +https_send_sms_SOURCES = https-send-sms.c +https_send_sms_LDADD = $(LIBCURL_LIBS) ipc_test_SOURCES = ipc-test.c ipc_test_LDADD = $(top_builddir)/samsung-ipc/libsamsung-ipc.la -ipc_test_LDFLAGS = nv_data_md5_SOURCES = nv_data-md5.c nv_data_md5_LDADD = $(top_builddir)/samsung-ipc/libsamsung-ipc.la -nv_data_md5_LDFLAGS = + +nv_data_imei_SOURCES = nv_data-imei.c nv_data-imei.h +nv_data_imei_LDADD = $(top_builddir)/samsung-ipc/libsamsung-ipc.la diff --git a/tools/https-send-sms.c b/tools/https-send-sms.c new file mode 100644 index 0000000..80ad00b --- /dev/null +++ b/tools/https-send-sms.c @@ -0,0 +1,205 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#include <curl/curl.h> + +/* CURL documentation for curl_easy_escape[1] mentions that "This + * function does not accept input strings longer than + * CURL_MAX_INPUT_LENGTH (8 MB)." but CURL_MAX_INPUT_LENGTH is not + * exported in the curl public headers (in /usr/include/curl). + */ +#define CURL_MAX_INPUT_LENGTH 8000000 + +long send_https(CURL *hnd, char *url, __attribute__((unused)) char *post_data) +{ + CURLcode ret; + long return_code; + + curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L); + curl_easy_setopt(hnd, CURLOPT_URL, url); + curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, + (long)CURL_HTTP_VERSION_2TLS); + curl_easy_setopt(hnd, CURLOPT_FTP_SKIP_PASV_IP, 1L); + curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L); + + ret = curl_easy_perform(hnd); + assert(ret == CURLE_OK); + + curl_easy_getinfo(hnd, CURLINFO_RESPONSE_CODE, &return_code); + + return return_code; +} + +int create_parameter(CURL *hnd, + char **out, const char *parameter, const char *value) +{ + char *result = NULL; + char *encoded_value; + size_t size = 0; + size_t rc; + + assert(strlen(value) < CURL_MAX_INPUT_LENGTH); + encoded_value = curl_easy_escape(hnd, value, 0); + assert(encoded_value != NULL); + + rc = snprintf(result, size, "%s=%s", parameter, encoded_value); + + assert(rc > 0); + size = rc; + + result = malloc(size + 1); + if (result == NULL) { + rc = -errno; + printf("%s: error %zd: %s\n", __func__, rc, strerror(rc)); + return rc; + } + + rc = snprintf(result, size + 1, "%s=%s", parameter, encoded_value); + assert(rc == size); + + assert(rc < CURL_MAX_INPUT_LENGTH); + + *out = result; + + curl_free(encoded_value); + + return 0; +} + +int send_sms_get(const char *username, const char *password, + const char *message) +{ + CURL *hnd; + char *parameter = NULL; + int rc; + CURLU *url; + char *url_string; + + url = curl_url(); + hnd = curl_easy_init(); + + rc = curl_url_set(url, CURLUPART_URL, + "https://smsapi.free-mobile.fr/sendmsg", 0); + assert(rc == 0); + + rc = create_parameter(hnd, ¶meter, "user", username); + assert(rc == 0); + rc = curl_url_set(url, CURLUPART_QUERY, parameter, CURLU_APPENDQUERY); + assert(rc == 0); + free(parameter); + + rc = create_parameter(hnd, ¶meter, "pass", password); + rc = curl_url_set(url, CURLUPART_QUERY, parameter, CURLU_APPENDQUERY); + assert(rc == 0); + free(parameter); + + rc = create_parameter(hnd, ¶meter, "msg", message); + rc = curl_url_set(url, CURLUPART_QUERY, parameter, CURLU_APPENDQUERY); + assert(rc == 0); + free(parameter); + + rc = curl_url_get(url, CURLUPART_URL, &url_string, 0); + assert(rc == 0); + + rc = send_https(hnd, url_string, NULL); + + switch (rc) { + case 200: + /* SMS SENT */ + rc = 0; + break; + case 400: + printf("Error %d: %s\n", + rc, + "Missing parameter (user, login, message)."); + rc = EX_USAGE; + break; + case 402: + printf("Error %d: %s\n", + rc, + "Too many SMS sent in too little time: Retry later.\n"); + rc = EX_TEMPFAIL; + break; + case 403: + printf("Error %d: %s\n", + rc, + "Possible causes:\n" + "- Service is not enabled in your account settings.\n" + "- Wrong login or password.\n"); + rc = EX_CONFIG; + break; + case 500: + printf("Error %d: %s\n", + rc, + "Service unavailable: Retry later"); + rc = EX_UNAVAILABLE; + break; + default: + printf("Unknown error %d.\n", rc); + rc = EX_PROTOCOL; + break; + } + + curl_easy_cleanup(hnd); + curl_url_cleanup(url); + + return rc; +} + +void usage(char *progname) +{ + printf("Usage: %s free-mobile <username> <token> <message>\n\n", + progname); + printf("Example:\n\t%s %s \"%s\" \"%s\" \"%s\"\n", + progname, + "free-mobile", + "12345678", + "1234abcdEFGH", + "hello world!"); +} + +int main(int argc, char *argv[]) +{ + char *message; + char *password; + char *username; + int rc; + + if (argc != 5 || strncmp("free-mobile", argv[1], + strlen("free-mobile"))) { + usage(argv[0]); + return EX_USAGE; + } + + username = argv[2]; + password = argv[3]; + message = argv[4]; + + rc = send_sms_get(username, password, message); + if (rc == 0) + printf("OK\n"); + return rc; +} diff --git a/tools/include/glibc/sysexits.h b/tools/include/glibc/sysexits.h new file mode 100644 index 0000000..37246b6 --- /dev/null +++ b/tools/include/glibc/sysexits.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)sysexits.h 8.1 (Berkeley) 6/2/93 + */ + +#ifndef _SYSEXITS_H +#define _SYSEXITS_H 1 + +/* + * SYSEXITS.H -- Exit status codes for system programs. + * + * This include file attempts to categorize possible error + * exit statuses for system programs, notably delivermail + * and the Berkeley network. + * + * Error numbers begin at EX__BASE to reduce the possibility of + * clashing with other exit statuses that random programs may + * already return. The meaning of the codes is approximately + * as follows: + * + * EX_USAGE -- The command was used incorrectly, e.g., with + * the wrong number of arguments, a bad flag, a bad + * syntax in a parameter, or whatever. + * EX_DATAERR -- The input data was incorrect in some way. + * This should only be used for user's data & not + * system files. + * EX_NOINPUT -- An input file (not a system file) did not + * exist or was not readable. This could also include + * errors like "No message" to a mailer (if it cared + * to catch it). + * EX_NOUSER -- The user specified did not exist. This might + * be used for mail addresses or remote logins. + * EX_NOHOST -- The host specified did not exist. This is used + * in mail addresses or network requests. + * EX_UNAVAILABLE -- A service is unavailable. This can occur + * if a support program or file does not exist. This + * can also be used as a catchall message when something + * you wanted to do doesn't work, but you don't know + * why. + * EX_SOFTWARE -- An internal software error has been detected. + * This should be limited to non-operating system related + * errors as possible. + * EX_OSERR -- An operating system error has been detected. + * This is intended to be used for such things as "cannot + * fork", "cannot create pipe", or the like. It includes + * things like getuid returning a user that does not + * exist in the passwd file. + * EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp, + * etc.) does not exist, cannot be opened, or has some + * sort of error (e.g., syntax error). + * EX_CANTCREAT -- A (user specified) output file cannot be + * created. + * EX_IOERR -- An error occurred while doing I/O on some file. + * EX_TEMPFAIL -- temporary failure, indicating something that + * is not really an error. In sendmail, this means + * that a mailer (e.g.) could not create a connection, + * and the request should be reattempted later. + * EX_PROTOCOL -- the remote system returned something that + * was "not possible" during a protocol exchange. + * EX_NOPERM -- You did not have sufficient permission to + * perform the operation. This is not intended for + * file system problems, which should use NOINPUT or + * CANTCREAT, but rather for higher level permissions. + */ + +#define EX_OK 0 /* successful termination */ + +#define EX__BASE 64 /* base value for error messages */ + +#define EX_USAGE 64 /* command line usage error */ +#define EX_DATAERR 65 /* data format error */ +#define EX_NOINPUT 66 /* cannot open input */ +#define EX_NOUSER 67 /* addressee unknown */ +#define EX_NOHOST 68 /* host name unknown */ +#define EX_UNAVAILABLE 69 /* service unavailable */ +#define EX_SOFTWARE 70 /* internal software error */ +#define EX_OSERR 71 /* system error (e.g., can't fork) */ +#define EX_OSFILE 72 /* critical OS file missing */ +#define EX_CANTCREAT 73 /* can't create (user) output file */ +#define EX_IOERR 74 /* input/output error */ +#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ +#define EX_PROTOCOL 76 /* remote error in protocol */ +#define EX_NOPERM 77 /* permission denied */ +#define EX_CONFIG 78 /* configuration error */ + +#define EX__MAX 78 /* maximum listed value */ + +#endif /* sysexits.h */ diff --git a/tools/ipc-modem.c b/tools/ipc-modem.c deleted file mode 100644 index 9314de6..0000000 --- a/tools/ipc-modem.c +++ /dev/null @@ -1,590 +0,0 @@ -/* - * This file is part of libsamsung-ipc. - * - * Copyright (C) 2011 Simon Busch <morphis@gravedo.de> - * Copyright (C) 2011 Paul Kocialkowsk <contact@paulk.fr> - * - * libsamsung-ipc is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * libsamsung-ipc is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <fcntl.h> -#include <getopt.h> -#include <pthread.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <termios.h> -#include <unistd.h> -#include <string.h> - -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include <samsung-ipc.h> - -#define MODEM_STATE_LPM 0 -#define MODEM_STATE_NORMAL 2 -#define MODEM_STATE_SIM_OK 4 - -#define DEF_CALL_NUMBER "950" -#define DEF_SIM_PIN "1234" - -int state = MODEM_STATE_LPM; -int seq; -int in_call; -int out_call; -int call_done; - -char sim_pin[8]; - -int seq_get(void) -{ - if (seq == 0xff) - seq = 0x00; - - seq++; - - return seq; -} - -void modem_snd_no_mic_mute(struct ipc_client *client) -{ - uint8_t data = 0; - - ipc_client_send(client, seq_get(), IPC_SND_MIC_MUTE_CTRL, IPC_TYPE_SET, - (void *) &data, 1); -} - -void modem_snd_clock_ctrl(struct ipc_client *client) -{ - uint8_t data = 0x01; - - ipc_client_send(client, seq_get(), IPC_SND_CLOCK_CTRL, IPC_TYPE_EXEC, - (void *) &data, 1); -} - -void modem_snd_spkr_volume_ctrl(struct ipc_client *client) -{ - uint16_t data = 0x0411; - - ipc_client_send(client, seq_get(), IPC_SND_SPKR_VOLUME_CTRL, - IPC_TYPE_SET, (void *) &data, 2); -} - -void modem_snd_audio_path_ctrl(struct ipc_client *client) -{ - uint8_t data = 0x01; - - ipc_client_send(client, seq_get(), IPC_SND_AUDIO_PATH_CTRL, - IPC_TYPE_SET, (void *) &data, 1); -} - - -void modem_exec_call_out(struct ipc_client *client, char *num) -{ - struct ipc_call_outgoing_data call_out; - - modem_snd_no_mic_mute(client); - - memset(&call_out, 0, sizeof(struct ipc_call_outgoing_data)); - - call_out.type = IPC_CALL_TYPE_VOICE; - call_out.identity = IPC_CALL_IDENTITY_DEFAULT; - call_out.number_length = strlen(num); - /* 0x21 = +33 */ - call_out.prefix = IPC_CALL_PREFIX_NONE; //0x21;//IPC_CALL_PREFIX_NONE; - memcpy(call_out.number, num, call_out.number_length); - - ipc_client_send(client, seq_get(), IPC_CALL_OUTGOING, IPC_TYPE_EXEC, - (void *) &call_out, - sizeof(struct ipc_call_outgoing_data)); - - out_call = 1; - - modem_snd_no_mic_mute(client); - modem_snd_spkr_volume_ctrl(client); - modem_snd_audio_path_ctrl(client); -} - -void modem_exec_call_answer(struct ipc_client *client) -{ - modem_snd_clock_ctrl(client); - - ipc_client_send(client, seq_get(), IPC_CALL_ANSWER, IPC_TYPE_EXEC, NULL, - 0); - - modem_snd_no_mic_mute(client); -} - -void modem_get_call_list(struct ipc_client *client) -{ - ipc_client_send(client, seq_get(), IPC_CALL_LIST, IPC_TYPE_GET, NULL, - 0); - - modem_snd_no_mic_mute(client); -} - -void modem_exec_power_normal(struct ipc_client *client) -{ - uint16_t data = 0x0202; - - ipc_client_send(client, seq_get(), IPC_PWR_PHONE_STATE, IPC_TYPE_EXEC, - (void *) &data, sizeof(data)); -} - -void modem_set_sms_device_ready(struct ipc_client *client) -{ - ipc_client_send(client, seq_get(), IPC_SMS_DEVICE_READY, IPC_TYPE_SET, - NULL, 0); -} - -void modem_set_sec_pin_status(struct ipc_client *client, char *pin1, char *pin2) -{ - struct ipc_sec_pin_status_request_data pin_status; - - printf("[I] Sending PIN1 unlock request\n"); - - ipc_sec_pin_status_setup(&pin_status, IPC_SEC_PIN_TYPE_PIN1, pin1, - pin2); - - ipc_client_send(client, seq_get(), IPC_SEC_PIN_STATUS, IPC_TYPE_SET, - (void *) &pin_status, sizeof(pin_status)); -} - -void modem_response_sec(struct ipc_client *client, struct ipc_message *resp) -{ - struct ipc_sec_pin_status_response_data *sim_status; - unsigned char type; - - switch (resp->command) { - case IPC_SEC_PIN_STATUS: - sim_status = - (struct ipc_sec_pin_status_response_data *)resp->data; - - switch (sim_status->status) { - case IPC_SEC_PIN_STATUS_CARD_NOT_PRESENT: - printf("[I] SIM card is definitely absent\n"); - break; - case IPC_SEC_PIN_STATUS_LOCK_SC: - switch (sim_status->facility_lock) { - case IPC_SEC_FACILITY_LOCK_TYPE_SC_PIN1_REQ: - printf("[I] " - "We need the PIN1 to unlock the card!" - "\n"); - if (strlen(sim_pin) > 0) { - modem_set_sec_pin_status(client, - sim_pin, NULL); - } else { - printf("[E] No SIM Pin, use --pin\n"); - } - break; - case IPC_SEC_FACILITY_LOCK_TYPE_SC_PUK_REQ: - printf("[I] Please provide the SIM card PUK!" - "\n"); - break; - case IPC_SEC_FACILITY_LOCK_TYPE_SC_CARD_BLOCKED: - printf("[I] Ouch, the SIM Card is blocked.\n"); - break; - } - break; - case IPC_SEC_PIN_STATUS_INIT_COMPLETE: - printf("[3] SIM init complete\n"); - if (state == MODEM_STATE_NORMAL) - state = MODEM_STATE_SIM_OK; - break; - case IPC_SEC_PIN_STATUS_PB_INIT_COMPLETE: - printf("[I] SIM Phone Book init complete\n"); - break; - } - break; - case IPC_SEC_SIM_ICC_TYPE: - type = *((char *) resp->data); - switch (type) { - case IPC_SEC_SIM_CARD_TYPE_UNKNOWN: - printf("[I] No SIM card type: unknown (absent?)\n"); - break; - case IPC_SEC_SIM_CARD_TYPE_SIM: - case IPC_SEC_SIM_CARD_TYPE_USIM: - printf("[I] SIM card found\n"); - break; - } - break; - } -} - -void modem_response_sms(struct ipc_client *client, struct ipc_message *resp) -{ - switch (resp->command) { - case IPC_SMS_DEVICE_READY: - if (state == MODEM_STATE_LPM) { - printf("[4] " - "Modem is ready, requesting normal power mode" - "\n"); - modem_exec_power_normal(client); - } else if (state == MODEM_STATE_SIM_OK) { - printf("[5] Modem is fully ready\n"); - modem_set_sms_device_ready(client); - } - break; - } -} - -void modem_response_call(struct ipc_client *client, struct ipc_message *resp) -{ - struct ipc_call_status_data *stat; - - switch (resp->command) { - case IPC_CALL_LIST: - /* - * if (in_call) - * modem_exec_call_answer(client); - * if (out_call) - * modem_snd_no_mic_mute(client); - */ - break; - case IPC_CALL_INCOMING: - printf("[I] Got an incoming call!\n"); - in_call = 1; - modem_get_call_list(client); - break; - case IPC_CALL_STATUS: - stat = (struct ipc_call_status_data *)resp->data; - - if (stat->status == IPC_CALL_STATUS_DIALING) { - printf("[I] Sending clock ctrl and restore alsa\n"); - modem_snd_clock_ctrl(client); - /* - * system("alsa_ctl -f /data/alsa_state_modem restore"); - */ - - printf("[I] CALL STATUS DIALING!!!\n"); - - modem_snd_spkr_volume_ctrl(client); - modem_snd_audio_path_ctrl(client); - - modem_get_call_list(client); - } - - if (stat->status == IPC_CALL_STATUS_CONNECTED) { - printf("[I] CALL STATUS CONNECTED!!!\n"); - modem_snd_no_mic_mute(client); - } - - if (stat->status == IPC_CALL_STATUS_RELEASED) { - printf("[I] CALL STATUS RELEASED!!!\n"); - modem_snd_no_mic_mute(client); - } - break; - } -} - -void modem_response_pwr(__attribute__((unused)) struct ipc_client *client, - struct ipc_message *resp) -{ - int state_n; - - switch (resp->command) { - case IPC_PWR_PHONE_PWR_UP: - printf("[2] Phone is powered up (LPM)!\n"); - state = MODEM_STATE_LPM; - break; - - case IPC_PWR_PHONE_STATE: - state_n = *((int *)resp->data); -#if 0 - switch (state_n) { - /* FIXME: Broken */ - case IPC_PWR_PHONE_STATE_NORMAL: - printf("Power state is now: NORMAL\n"); - break; - case IPC_PWR_PHONE_STATE_LPM: - printf("Power state is now: LPM (Low Power Mode)?\n"); - break; - } -#endif - state = state_n; - break; - - } -} - -void modem_response_net(__attribute__((unused)) struct ipc_client *client, - struct ipc_message *resp) -{ - struct ipc_net_regist_response_data *regi; - char mnc[6]; - - switch (resp->command) { - case IPC_NET_REGIST: - regi = (struct ipc_net_regist_response_data *) resp->data; - if (regi->status == IPC_NET_REGISTRATION_STATUS_HOME) - printf("[I] Registered with network successfully!\n"); - break; - case IPC_NET_SERVING_NETWORK: - memcpy(mnc, (char *)((char *) resp->data + 3), 5); - mnc[5] = 0; - printf("[6] Registered with network! " - "Got PLMN (Mobile Network Code): '%s'\n", - mnc); - /* - * if (call_done == 0) - * { - * printf("Requesting outgoing call to %s!\n", DEF_CALL_NUMBER); - * modem_exec_call_out(client, DEF_CALL_NUMBER); - * } - * call_done = 1; - */ - break; - } -} - -void modem_response_handle(struct ipc_client *client, struct ipc_message *resp) -{ - switch (IPC_GROUP(resp->command)) { - case IPC_GROUP_NET: - modem_response_net(client, resp); - break; - case IPC_GROUP_PWR: - modem_response_pwr(client, resp); - break; - case IPC_GROUP_SEC: - modem_response_sec(client, resp); - break; - case IPC_GROUP_SMS: - modem_response_sms(client, resp); - break; - case IPC_GROUP_CALL: - modem_response_call(client, resp); - break; - case IPC_GROUP_DISP: - if (in_call) - modem_snd_no_mic_mute(client); - break; - } -} - - -int modem_read_loop(struct ipc_client *client) -{ - struct ipc_message resp; - int rc; - - memset(&resp, 0, sizeof(resp)); - - while (1) { - usleep(3000); - - rc = ipc_client_poll(client, NULL, NULL); - if (rc < 0) - continue; - - rc = ipc_client_recv(client, &resp); - if (rc < 0) { - printf("[E] " - "Can't RECV from modem: please run this again" - "\n"); - break; - } - - modem_response_handle(client, &resp); - - if (resp.data != NULL) - free(resp.data); - } - - return 0; -} - -void modem_log_handler(__attribute__((unused)) void *user_data, - const char *msg) -{ - int i, l; - char *message; - - message = strdup(msg); - l = strlen(message); - - if (l > 1) { - for (i = l ; i > 0 ; i--) { - if (message[i] == '\n') - message[i] = 0; - else if (message[i] != 0) - break; - } - - printf("[D] %s\n", message); - } - - free(message); -} - -void modem_log_handler_quiet(__attribute__((unused)) void *user_data, - __attribute__((unused)) const char *msg) -{ -} - -int modem_start(struct ipc_client *client) -{ - int rc = -1; - - ipc_client_data_create(client); - rc = ipc_client_boot(client); - if (rc < 0) - return -1; - - usleep(300); - - rc = ipc_client_open(client); - if (rc < 0) - return -1; - - rc = ipc_client_power_on(client); - if (rc < 0) - return -1; - - return 0; -} - -int modem_stop(struct ipc_client *client) -{ - ipc_client_power_off(client); - ipc_client_close(client); - - return 0; -} - -void print_help(void) -{ - printf("usage: ipc-modem <command>\n"); - printf("commands:\n"); - printf("\tstart boot modem and start read loop\n"); - printf("\tboot boot modem only\n"); - printf("\tpower-on power on the modem\n"); - printf("\tpower-off power off the modem\n"); - printf("arguments:\n"); - printf("\t--debug enable debug messages\n"); - printf("\t--pin=[PIN] provide SIM card PIN\n"); -} - -int main(int argc, char *argv[]) -{ - struct ipc_client *client_fmt; - int c = 0; - int opt_i = 0; - int rc = -1; - int debug = 0; - - struct option opt_l[] = { - {"help", no_argument, 0, 0 }, - {"debug", no_argument, 0, 0 }, - {"pin", required_argument, 0, 0 }, - {0, 0, 0, 0 } - }; - - if (argc < 2) { - print_help(); - exit(1); - } - - while (c >= 0) { - c = getopt_long(argc, argv, "", opt_l, &opt_i); - if (c < 0) - break; - - switch (c) { - case 0: - if (strncmp(opt_l[opt_i].name, "help", 4) == 0) { - print_help(); - exit(1); - } else if (strcmp(opt_l[opt_i].name, "debug") == 0) { - debug = 1; - printf("[I] Debug enabled\n"); - } else if (strcmp(opt_l[opt_i].name, "pin") == 0) { - if (optarg) { - if (strlen(optarg) < 8) { - printf("[I] Got SIM PIN!\n"); - memcpy(sim_pin, optarg, 8); - } else { - printf("[E] " - "SIM PIN is too long!" - "\n"); - return 1; - } - } - } - break; - } - } - - client_fmt = ipc_client_create(IPC_CLIENT_TYPE_FMT); - - if (client_fmt == 0) { - printf("[E] Could not create IPC client; aborting ...\n"); - goto modem_quit; - } - - if (debug == 0) { - ipc_client_log_callback_register(client_fmt, - modem_log_handler_quiet, NULL); - } else { - ipc_client_log_callback_register(client_fmt, modem_log_handler, - NULL); - } - - while (optind < argc) { - if (strncmp(argv[optind], "power-on", 8) == 0) { - if (ipc_client_power_on(client_fmt) < 0) - printf("[E] Something went wrong " - "while powering modem on\n"); - goto modem_quit; - } else if (strncmp(argv[optind], "power-off", 9) == 0) { - if (ipc_client_power_off(client_fmt) < 0) - printf("[E] Something went wrong " - "while powering modem off\n"); - goto modem_quit; - } else if (strncmp(argv[optind], "boot", 9) == 0) { - rc = ipc_client_boot(client_fmt); - if (rc < 0) - printf("[E] Something went wrong " - "while bootstrapping modem\n"); - } else if (strncmp(argv[optind], "start", 5) == 0) { - printf("[0] Starting modem on FMT client\n"); - rc = modem_start(client_fmt); - if (rc < 0) { - printf("[E] Something went wrong\n"); - modem_stop(client_fmt); - return 1; - } - - printf("[1] Starting modem_read_loop on FMT client\n"); - modem_read_loop(client_fmt); - - modem_stop(client_fmt); - } else { - printf("[E] Unknown argument: '%s'\n", argv[optind]); - print_help(); - return 1; - } - - optind++; - } - -modem_quit: - if (client_fmt != 0) - ipc_client_destroy(client_fmt); - - return 0; -} diff --git a/tools/ipc-modem/Makefile.am b/tools/ipc-modem/Makefile.am new file mode 100644 index 0000000..64cde49 --- /dev/null +++ b/tools/ipc-modem/Makefile.am @@ -0,0 +1,28 @@ +NULL = +EXTRA_DIST = $(NULL) + +AM_CFLAGS = \ + -I$(top_srcdir)/include \ + $(NULL) + +if WANT_STRICT_CFLAGS +AM_CFLAGS += $(STRICT_CFLAGS) +endif + +bin_PROGRAMS = ipc-modem + +# TODO: Find a way to make test more modular and represent each run of the +# ipc-modem in TEST while having it implemented in a single python file +PY_LOG_COMPILER = $(PYTHON3) +TEST_EXTENSIONS = .py +TESTS = tests/ipc-modem.py +EXTRA_DIST += $(TESTS) + +ipc_modem_SOURCES = \ +ipc-modem.c \ +ipc-modem.h \ +ipc-modem-log.c \ +ipc-modem-log.h + +ipc_modem_LDADD = $(top_builddir)/samsung-ipc/libsamsung-ipc.la +ipc_modem_LDFLAGS = -lpthread $(ALL_STATIC_LDFLAGS) diff --git a/tools/ipc-modem/ipc-modem-log.c b/tools/ipc-modem/ipc-modem-log.c new file mode 100644 index 0000000..7e6b48d --- /dev/null +++ b/tools/ipc-modem/ipc-modem-log.c @@ -0,0 +1,84 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2011 Simon Busch <morphis@gravedo.de> + * Copyright (C) 2011 Paul Kocialkowsk <contact@paulk.fr> + * Copyright (C) 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "ipc-modem-log.h" + +static enum log_target log_target; + +void modem_log_handler(void *user_data, const char *msg) +{ + int i, l; + char *message; + + message = strdup(msg); + l = strlen(message); + + if (l > 1) { + for (i = l ; i > 0 ; i--) { + if (message[i] == '\n') + message[i] = 0; + else if (message[i] != 0) + break; + } + + if (log_target == LOG_TO_STDOUT && user_data) + printf("[%s] %s\n", (char *)user_data, message); + else if (log_target == LOG_TO_STDOUT) + printf("[%s] %s\n", MODEM_LOG_DEBUG, message); + else if (log_target == LOG_TO_SYSLOG && user_data) + syslog(LOG_INFO, + "[%s] %s\n", (char *)user_data, message); + else if (log_target == LOG_TO_SYSLOG) + syslog(LOG_INFO, "[%s] %s\n", MODEM_LOG_DEBUG, message); + } + + free(message); +} + +void modem_log_handler_quiet(__attribute__((unused)) void *user_data, + __attribute__((unused)) const char *msg) +{ +} + +void ipc_modem_log(__attribute__((unused)) struct ipc_client *client, + char *prefix, const char *message, ...) +{ + va_list args; + char buffer[4096]; + + va_start(args, message); + vsnprintf((char *) &buffer, sizeof(buffer), message, args); + va_end(args); + + modem_log_handler(prefix, buffer); +} + +void ipc_modem_set_log_target(enum log_target target) +{ + log_target = target; +} + diff --git a/tools/ipc-modem/ipc-modem-log.h b/tools/ipc-modem/ipc-modem-log.h new file mode 100644 index 0000000..1786295 --- /dev/null +++ b/tools/ipc-modem/ipc-modem-log.h @@ -0,0 +1,41 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef IPC_MODEM_LOG_H +#define IPC_MODEM_LOG_H + +#include <stdarg.h> + +#include <samsung-ipc.h> + +#define MODEM_LOG_ERROR "E" +#define MODEM_LOG_INFO "I" +#define MODEM_LOG_DEBUG "D" + +enum log_target { + LOG_TO_STDOUT, + LOG_TO_SYSLOG, +}; + +void ipc_modem_log(struct ipc_client *client, + char *prefix, const char *message, ...); +void modem_log_handler(void *user_data, const char *msg); +void modem_log_handler_quiet(void *user_data, const char *msg); +void ipc_modem_set_log_target(enum log_target target); + +#endif /* IPC_MODEM_LOG_H */ diff --git a/tools/ipc-modem/ipc-modem.c b/tools/ipc-modem/ipc-modem.c new file mode 100644 index 0000000..c70bcc1 --- /dev/null +++ b/tools/ipc-modem/ipc-modem.c @@ -0,0 +1,781 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2011 Simon Busch <morphis@gravedo.de> + * Copyright (C) 2011 Paul Kocialkowsk <contact@paulk.fr> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <fcntl.h> +#include <getopt.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <sysexits.h> +#include <termios.h> +#include <unistd.h> +#include <string.h> + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <samsung-ipc.h> + +#include "ipc-modem.h" +#include "ipc-modem-log.h" + +int seq_get(struct ipc_modem_data *data) +{ + if (data->seq == 0xff) + data->seq = 0x00; + + data->seq++; + + return data->seq; +} + +void modem_snd_no_mic_mute(struct ipc_modem_data *data) +{ + uint8_t mic_mute_data = 0; + + ipc_client_send(data->client, + seq_get(data), IPC_SND_MIC_MUTE_CTRL, IPC_TYPE_SET, + (void *) &mic_mute_data, 1); +} + +void modem_snd_clock_ctrl(struct ipc_modem_data *data) +{ + uint8_t clock_ctrl_data = 0x01; + + ipc_client_send(data->client, + seq_get(data), IPC_SND_CLOCK_CTRL, IPC_TYPE_EXEC, + (void *) &clock_ctrl_data, 1); +} + +void modem_snd_spkr_volume_ctrl(struct ipc_modem_data *data) +{ + uint16_t spkr_volume_data = 0x0411; + + ipc_client_send(data->client, seq_get(data), IPC_SND_SPKR_VOLUME_CTRL, + IPC_TYPE_SET, (void *) &spkr_volume_data, 2); +} + +void modem_snd_audio_path_ctrl(struct ipc_modem_data *data) +{ + uint8_t audio_path_ctrl_data = 0x01; + + ipc_client_send(data->client, seq_get(data), IPC_SND_AUDIO_PATH_CTRL, + IPC_TYPE_SET, (void *) &audio_path_ctrl_data, 1); +} + + + +void modem_exec_call_out(struct ipc_modem_data *data, char *num) +{ + struct ipc_call_outgoing_data call_out; + + modem_snd_no_mic_mute(data); + + memset(&call_out, 0, sizeof(struct ipc_call_outgoing_data)); + + call_out.type = IPC_CALL_TYPE_VOICE; + call_out.identity = IPC_CALL_IDENTITY_DEFAULT; + call_out.number_length = strlen(num); + /* TODO: Add support for prefixes */ + /* 0x21 = +33 */ + call_out.prefix = IPC_CALL_PREFIX_NONE; //0x21;//IPC_CALL_PREFIX_NONE; + memcpy(call_out.number, num, call_out.number_length); + + ipc_client_send(data->client, + seq_get(data), IPC_CALL_OUTGOING, IPC_TYPE_EXEC, + (void *) &call_out, + sizeof(struct ipc_call_outgoing_data)); + + data->out_call = 1; + + modem_snd_no_mic_mute(data); + modem_snd_spkr_volume_ctrl(data); + modem_snd_audio_path_ctrl(data); +} + +void modem_exec_call_answer(struct ipc_modem_data *data) +{ + modem_snd_clock_ctrl(data); + + ipc_client_send(data->client, + seq_get(data), IPC_CALL_ANSWER, IPC_TYPE_EXEC, NULL, 0); + + modem_snd_no_mic_mute(data); +} + +void modem_get_call_list(struct ipc_modem_data *data) +{ + ipc_client_send(data->client, + seq_get(data), IPC_CALL_LIST, IPC_TYPE_GET, NULL, 0); + + modem_snd_no_mic_mute(data); +} + +void modem_exec_power_normal(struct ipc_modem_data *data) +{ + uint16_t exec_data = IPC_PWR_PHONE_STATE_REQUEST_NORMAL; + + ipc_client_send(data->client, + seq_get(data), IPC_PWR_PHONE_STATE, IPC_TYPE_EXEC, + (void *) &exec_data, sizeof(data)); +} + +void modem_set_sms_device_ready(struct ipc_modem_data *data) +{ + ipc_client_send(data->client, + seq_get(data), IPC_SMS_DEVICE_READY, IPC_TYPE_SET, + NULL, 0); +} + +void modem_set_sec_pin_status(struct ipc_modem_data *data, + char *pin1, char *pin2) +{ + struct ipc_sec_pin_status_request_data pin_status; + + ipc_modem_log(data->client, + MODEM_LOG_INFO, "Sending PIN1 unlock request\n"); + + ipc_sec_pin_status_setup(&pin_status, IPC_SEC_PIN_TYPE_PIN1, pin1, + pin2); + + ipc_client_send(data->client, + seq_get(data), IPC_SEC_PIN_STATUS, IPC_TYPE_SET, + (void *) &pin_status, sizeof(pin_status)); +} + +void modem_response_sec(struct ipc_modem_data *data, struct ipc_message *resp) +{ + struct ipc_sec_pin_status_response_data *sim_status; + unsigned char type; + + switch (resp->command) { + case IPC_SEC_PIN_STATUS: + sim_status = + (struct ipc_sec_pin_status_response_data *)resp->data; + + switch (sim_status->status) { + case IPC_SEC_PIN_STATUS_CARD_NOT_PRESENT: + ipc_modem_log(data->client, MODEM_LOG_INFO, + "SIM card is definitely absent\n"); + break; + case IPC_SEC_PIN_STATUS_LOCK_SC: + switch (sim_status->facility_lock) { + case IPC_SEC_FACILITY_LOCK_TYPE_SC_PIN1_REQ: + ipc_modem_log( + data->client, + MODEM_LOG_INFO, + "We need the PIN1 to unlock the card!" + "\n"); + if (strlen(data->sim_pin) > 0) { + modem_set_sec_pin_status(data, + data->sim_pin, + NULL); + } else { + ipc_modem_log( + data->client, + MODEM_LOG_ERROR, + "No SIM Pin, use --pin\n"); + } + break; + case IPC_SEC_FACILITY_LOCK_TYPE_SC_PUK_REQ: + ipc_modem_log( + data->client, + MODEM_LOG_INFO, + "Please provide the SIM card PUK!" + "\n"); + break; + case IPC_SEC_FACILITY_LOCK_TYPE_SC_CARD_BLOCKED: + ipc_modem_log( + data->client, + MODEM_LOG_INFO, + "Ouch, the SIM Card is blocked.\n"); + break; + } + break; + case IPC_SEC_PIN_STATUS_INIT_COMPLETE: + ipc_modem_log(data->client, "3", "SIM init complete\n"); + if (data->state == MODEM_STATE_NORMAL) + data->state = MODEM_STATE_SIM_OK; + break; + case IPC_SEC_PIN_STATUS_PB_INIT_COMPLETE: + ipc_modem_log(data->client, + MODEM_LOG_INFO, + "SIM Phone Book init complete\n"); + break; + } + break; + case IPC_SEC_SIM_ICC_TYPE: + type = *((char *) resp->data); + switch (type) { + case IPC_SEC_SIM_CARD_TYPE_UNKNOWN: + ipc_modem_log( + data->client, + MODEM_LOG_INFO, + "No SIM card type: unknown (absent?)\n"); + break; + case IPC_SEC_SIM_CARD_TYPE_SIM: + case IPC_SEC_SIM_CARD_TYPE_USIM: + ipc_modem_log(data->client, + MODEM_LOG_INFO, "SIM card found\n"); + break; + } + break; + } +} + +void modem_response_sms(struct ipc_modem_data *data, struct ipc_message *resp) +{ + switch (resp->command) { + case IPC_SMS_DEVICE_READY: + if (data->state == MODEM_STATE_LPM) { + ipc_modem_log( + data->client, + "4", + "Modem is ready, requesting normal power mode" + "\n"); + modem_exec_power_normal(data); + } else if (data->state == MODEM_STATE_SIM_OK) { + ipc_modem_log(data->client, + "5", "Modem is fully ready\n"); + modem_set_sms_device_ready(data); + } + break; + } +} + +void modem_response_call(struct ipc_modem_data *data, struct ipc_message *resp) +{ + struct ipc_call_status_data *stat; + + switch (resp->command) { + case IPC_CALL_LIST: + /* + * if (data->in_call) + * modem_exec_call_answer(data->client); + * if (data->out_call) + * modem_snd_no_mic_mute(data); + */ + break; + case IPC_CALL_INCOMING: + ipc_modem_log(data->client, + MODEM_LOG_INFO, "Got an incoming call!\n"); + data->in_call = 1; + modem_get_call_list(data); + break; + case IPC_CALL_STATUS: + stat = (struct ipc_call_status_data *)resp->data; + + if (stat->status == IPC_CALL_STATUS_DIALING) { + ipc_modem_log( + data->client, + MODEM_LOG_INFO, + "Sending clock ctrl and restore alsa\n"); + modem_snd_clock_ctrl(data); + /* + * system("alsa_ctl -f /data/alsa_state_modem restore"); + */ + + ipc_modem_log(data->client, + MODEM_LOG_INFO, + "CALL STATUS DIALING!!!\n"); + + modem_snd_spkr_volume_ctrl(data); + modem_snd_audio_path_ctrl(data); + + modem_get_call_list(data); + } + + if (stat->status == IPC_CALL_STATUS_CONNECTED) { + ipc_modem_log(data->client, + MODEM_LOG_INFO, + "CALL STATUS CONNECTED!!!\n"); + modem_snd_no_mic_mute(data); + } + + if (stat->status == IPC_CALL_STATUS_RELEASED) { + ipc_modem_log(data->client, + MODEM_LOG_INFO, + "CALL STATUS RELEASED!!!\n"); + modem_snd_no_mic_mute(data); + } + break; + } +} + +void modem_response_pwr(struct ipc_modem_data *data, struct ipc_message *resp) +{ + int state_n; + + switch (resp->command) { + case IPC_PWR_PHONE_PWR_UP: + ipc_modem_log(data->client, + "2", "Phone is powered up (LPM)!\n"); + data->state = MODEM_STATE_LPM; + break; + + case IPC_PWR_PHONE_STATE: + state_n = *((int *)resp->data); +#if 0 + switch (state_n) { + /* FIXME: Broken */ + case IPC_PWR_PHONE_STATE_NORMAL: + ipc_modem_log(data->client, + MODEM_LOG_INFO, + "Power state is now: NORMAL\n"); + break; + case IPC_PWR_PHONE_STATE_LPM: + ipc_modem_log( + data->client, + MODEM_LOG_INFO, + "Power state is now: LPM (Low Power Mode)?\n"); + break; + } +#endif + data->state = state_n; + break; + + } +} + +void modem_response_net(struct ipc_modem_data *data, + struct ipc_message *resp) +{ + struct ipc_net_regist_response_data *regi; + char mnc[6]; + + switch (resp->command) { + case IPC_NET_REGIST: + regi = (struct ipc_net_regist_response_data *) resp->data; + if (regi->status == IPC_NET_REGISTRATION_STATUS_HOME) + ipc_modem_log( + data->client, + MODEM_LOG_INFO, + "Registered with network successfully!\n"); + break; + case IPC_NET_SERVING_NETWORK: + memcpy(mnc, (char *)((char *) resp->data + 3), 5); + mnc[5] = 0; + ipc_modem_log(data->client, + "6", + "Registered with network! " + "Got PLMN (Mobile Network Code): '%s'\n", + mnc); + if (data->call_done == 0) { + ipc_modem_log(data->client, + MODEM_LOG_INFO, + "Requesting outgoing call to %s!\n", + data->call_number); + modem_exec_call_out(data, data->call_number); + } + data->call_done = 1; + break; + } +} + +void modem_response_handle(struct ipc_modem_data *data, + struct ipc_message *resp) +{ + switch (IPC_GROUP(resp->command)) { + case IPC_GROUP_NET: + modem_response_net(data, resp); + break; + case IPC_GROUP_PWR: + modem_response_pwr(data, resp); + break; + case IPC_GROUP_SEC: + modem_response_sec(data, resp); + break; + case IPC_GROUP_SMS: + modem_response_sms(data, resp); + break; + case IPC_GROUP_CALL: + modem_response_call(data, resp); + break; + case IPC_GROUP_DISP: + if (data->in_call) + modem_snd_no_mic_mute(data); + break; + } +} + + +int modem_read_loop(struct ipc_modem_data *data) +{ + struct ipc_message resp; + int rc; + + memset(&resp, 0, sizeof(resp)); + + while (1) { + usleep(3000); + + rc = ipc_client_poll(data->client, NULL, NULL); + if (rc < 0) + continue; + + rc = ipc_client_recv(data->client, &resp); + if (rc < 0) { + ipc_modem_log( + data->client, + MODEM_LOG_ERROR, + "Can't RECV from modem: please run this again" + "\n"); + break; + } + + modem_response_handle(data, &resp); + + if (resp.data != NULL) + free(resp.data); + } + + return 0; +} + +int modem_dummy_read_loop(__attribute__((unused)) struct ipc_client *client) +{ + while (true) { + ipc_modem_log(client, + MODEM_LOG_INFO, "%s: looping\n", __func__); + sleep(1); + } + + return 0; +} + +int modem_start(struct ipc_client *client) +{ + int rc = -1; + + ipc_client_data_create(client); + rc = ipc_client_boot(client); + if (rc < 0) + return -1; + + usleep(300); + + rc = ipc_client_open(client); + if (rc < 0) + return -1; + + rc = ipc_client_power_on(client); + if (rc < 0) + return -1; + + return 0; +} + +int modem_stop(struct ipc_client *client) +{ + ipc_client_power_off(client); + ipc_client_close(client); + + return 0; +} + +void print_help(void) +{ + printf("usage: ipc-modem <command>\n" + "commands:\n" + "\tboot boot modem only\n" + "\tpower-on power on the modem only\n" + "\tpower-off power off the modem only\n" + "\tstart " + "boot modem and start read loop\n" + "arguments:\n" + "\t--call=<NUMBER> call NUMBER\n" + "\t--debug enable debug messages\n" + "\t--dry-run " + "Test the ipc-modem program without talking to the modem.\n" + "\t--help print this help message\n" + "\t--log-target=[stdout|syslog] " + "direct logs to stdout or syslog\n" + "\t--pin=<PIN> provide SIM card PIN\n"); +} + +int handle_command(struct ipc_modem_data *data) +{ + int rc = 0; + + switch (data->command) { + case CMD_POWER_ON: + if (data->dry_run) + break; + + rc = ipc_client_power_on(data->client); + if (rc < 0) + ipc_modem_log(data->client, "[E]", + "[E] Something went wrong " + "while powering modem on\n"); + goto modem_quit; + case CMD_POWER_OFF: + if (data->dry_run) + break; + + rc = ipc_client_power_off(data->client); + if (rc < 0) + ipc_modem_log(data->client, + MODEM_LOG_ERROR, + "Something went wrong " + "while powering modem off\n"); + goto modem_quit; + case CMD_BOOT: + if (data->dry_run) + break; + + rc = ipc_client_boot(data->client); + if (rc < 0) + ipc_modem_log(data->client, + MODEM_LOG_ERROR, + "Something went wrong " + "while bootstrapping modem\n"); + break; + case CMD_START: + if (data->dry_run) { + ipc_modem_log( + data->client, + "1", + "Starting dummy modem_read_loop on %s client\n", + "FMT"); + modem_dummy_read_loop(data->client); + break; + } + + ipc_modem_log(data->client, + "0", "Starting modem on FMT client\n"); + + rc = modem_start(data->client); + if (rc < 0) { + ipc_modem_log(data->client, + MODEM_LOG_ERROR, + "Something went wrong\n"); + modem_stop(data->client); + return EX_UNAVAILABLE; + } + ipc_modem_log(data->client, + "1", + "Starting modem_read_loop on FMT client\n"); + modem_read_loop(data); + + modem_stop(data->client); + break; + default: + /* We should handle all commands */ + ipc_modem_log(data->client, + MODEM_LOG_ERROR, + "%s: Unknown command %d\n", __func__, + data->command); + + assert(false); + } + +modem_quit: + if (data->client != 0) + ipc_client_destroy(data->client); + + return rc; +} + +void print_cmdline_opts(struct ipc_modem_data *data) + +{ + if (data->debug) + ipc_modem_log(data->client, MODEM_LOG_INFO, "Debug enabled\n"); + if (data->debug && data->dry_run) + ipc_modem_log(data->client, MODEM_LOG_INFO, "dry-run mode\n"); +} + +int parse_cmdline_opts(struct ipc_modem_data *data, int argc, char *argv[]) +{ + int c = 0; + int opt_i = 0; + + struct option opt_l[] = { + {"call", required_argument, 0, 0 }, + {"debug", no_argument, 0, 0 }, + {"dry-run", no_argument, 0, 0 }, + {"help", no_argument, 0, 0 }, + {"log-target", required_argument, 0, 0 }, + {"pin", required_argument, 0, 0 }, + {0, 0, 0, 0 } + }; + + if (argc < 2) { + print_help(); + exit(EX_USAGE); + } + + /* Handle options arguments */ + while (true) { + c = getopt_long(argc, argv, "", opt_l, &opt_i); + if (c != 0) + break; + + if (strcmp(opt_l[opt_i].name, "call") == 0) { + if (optarg) { + if (strlen(optarg) == 0) { + ipc_modem_log(data->client, + MODEM_LOG_ERROR, + "Missing call number\n"); + return EX_USAGE; + } else if (strlen(optarg) < 14) { + assert(strlen(optarg) < + sizeof(data->call_number)); + ipc_modem_log(data->client, + MODEM_LOG_INFO, + "Got call number!\n"); + strcpy(data->call_number, optarg); + } else { + ipc_modem_log( + data->client, + MODEM_LOG_ERROR, + "Call number is too long!\n"); + return EX_USAGE; + } + } + } else if ((strcmp(opt_l[opt_i].name, "log-target") == 0)) { + if (optarg) { + if (!strcmp(optarg, "syslog")) { + ipc_modem_set_log_target(LOG_TO_SYSLOG); + } else if (strcmp(optarg, "stdout")) { + ipc_modem_log( + data->client, MODEM_LOG_ERROR, + "Invalid log target '%s'\n", + optarg); + return EX_USAGE; + } + } + } else if (strcmp(opt_l[opt_i].name, "debug") == 0) { + data->debug = true; + } else if (strcmp(opt_l[opt_i].name, "dry-run") == 0) { + data->dry_run = true; + } else if (strncmp(opt_l[opt_i].name, "help", 4) == 0) { + print_help(); + exit(0); + } else if ((strcmp(opt_l[opt_i].name, "pin") == 0) && + (optarg)) { + if (strlen(optarg) < 8) { + assert(strlen(optarg) < + sizeof(data->sim_pin)); + + ipc_modem_log( + data->client, + MODEM_LOG_INFO, "Got SIM PIN!\n"); + strcpy(data->sim_pin, optarg); + } else { + ipc_modem_log(data->client, + MODEM_LOG_ERROR, + "SIM PIN is too long!\n"); + return EX_USAGE; + } + } + } + + /* Handle non options arguments */ + while (optind < argc) { + if (strncmp(argv[optind], "boot", 9) == 0) { + data->command = CMD_BOOT; + break; + } else if (strncmp(argv[optind], "power-on", 8) == 0) { + data->command = CMD_POWER_ON; + break; + } else if (strncmp(argv[optind], "power-off", 9) == 0) { + data->command = CMD_POWER_OFF; + break; + } else if (strncmp(argv[optind], "start", 5) == 0) { + data->command = CMD_START; + break; + } else { + ipc_modem_log(data->client, + MODEM_LOG_ERROR, + "Unknown argument: '%s'\n", + argv[optind]); + print_help(); + return EX_USAGE; + } + + optind++; + } + + if (data->command == CMD_NONE) { + ipc_modem_log( + data->client, MODEM_LOG_ERROR, + "\n" + "Error: No command given. You need to use a command.\n" + " See the help below for more details.\n" + "\n"); + print_help(); + return EX_USAGE; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct ipc_modem_data data; + int ret; + + bzero((void *)&data, sizeof(data)); + + data.client = ipc_client_create(IPC_CLIENT_TYPE_DUMMY); + + ret = parse_cmdline_opts(&data, argc, argv); + if (ret) + return ret; + + if (!data.dry_run) { + ipc_client_destroy(data.client); + data.client = ipc_client_create(IPC_CLIENT_TYPE_FMT); + + if (data.client == 0) { + data.client = ipc_client_create(IPC_CLIENT_TYPE_DUMMY); + if (data.client) + ipc_modem_log( + data.client, + MODEM_LOG_ERROR, + "Could not create IPC client; " + "aborting ...\n"); + else + printf("Could not create IPC client; " + "aborting ...\n"); + + return EX_UNAVAILABLE; + } + } + + if (data.debug == 0) + ipc_client_log_callback_register(data.client, + modem_log_handler_quiet, + NULL); + else + ipc_client_log_callback_register(data.client, + modem_log_handler, + NULL); + + print_cmdline_opts(&data); + + return handle_command(&data); +} diff --git a/tools/ipc-modem/ipc-modem.h b/tools/ipc-modem/ipc-modem.h new file mode 100644 index 0000000..0ba2f8e --- /dev/null +++ b/tools/ipc-modem/ipc-modem.h @@ -0,0 +1,51 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2011 Simon Busch <morphis@gravedo.de> + * Copyright (C) 2011 Paul Kocialkowsk <contact@paulk.fr> + * Copyright (C) 2022 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef IPC_MODEM_H +#define IPC_MODEM_H + +#define MODEM_STATE_LPM 0 +#define MODEM_STATE_NORMAL 2 +#define MODEM_STATE_SIM_OK 4 + +enum command { + CMD_NONE, + CMD_START, + CMD_BOOT, + CMD_POWER_ON, + CMD_POWER_OFF, +}; + +struct ipc_modem_data { + struct ipc_client *client; + char call_number[14]; + char sim_pin[8]; + enum command command; + bool debug; + bool dry_run; + /* State */ + bool call_done; + bool in_call; + bool out_call; + int state; + int seq; +}; + +#endif /* IPC_MODEM */ diff --git a/tools/ipc-modem/tests/ipc-modem.py b/tools/ipc-modem/tests/ipc-modem.py new file mode 100755 index 0000000..d04f40d --- /dev/null +++ b/tools/ipc-modem/tests/ipc-modem.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# +# This file is part of libsamsung-ipc. +# +# Copyright (C) 2020 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> +# +# libsamsung-ipc is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# libsamsung-ipc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + +import os +import re +import sys +import sh + +# sysexits.h +class SysExit(object): + #define EX_USAGE 64 /* command line usage error */ + EX_USAGE = sh.ErrorReturnCode_64 + +def usage(progname): + print('{} [test]'.format(progname)) + sys.exit(1) + +def get_output(data): + return str(data).replace(os.linesep, '') + +class IpcModem(object): + def __init__(self): + srcdir = os.environ.get('srcdir', None) + + command_path = '' + if srcdir: + command_path = '.' + os.sep + 'ipc-modem' + # Enable to run tests without automake + else: + command_path = os.path.dirname(sys.argv[0]) \ + + os.sep \ + + '..' \ + + os.sep \ + + 'ipc-modem' + + if 'VALGRIND' in os.environ: + self.timeout = 60 + ipc_modem = sh.Command(os.environ['VALGRIND']) + self.ipc_modem = ipc_modem.bake('-v', + '--log-file=valgrind.%p.log', + '--leak-check=full', + command_path, + '--dry-run') + else: + self.timeout = 3 + ipc_modem = sh.Command(command_path) + self.ipc_modem = ipc_modem.bake('--dry-run') + + def test_help(self): + try: + self.ipc_modem() + except SysExit.EX_USAGE: + pass + else: + raise Exception() + + def test_boot(self, timeout=None): + if timeout==None: + timeout = self.timeout + + self.ipc_modem('boot', _timeout=timeout) + + def test_power_on(self, timeout=None): + if timeout==None: + timeout = self.timeout + + self.ipc_modem('power-on', _timeout=timeout) + + def test_power_off(self, timeout=None): + if timeout==None: + timeout = self.timeout + + self.ipc_modem('power-off', _timeout=timeout) + + def test_start(self, timeout=None): + if timeout==None: + timeout = self.timeout + + try: + self.ipc_modem('start', _timeout=timeout) + except sh.TimeoutException: + pass + else: + raise Exception() + + def test_call_with_number(self, timeout=None): + if timeout==None: + timeout = self.timeout + + expected_output = "[I] Got call number!" + output = "" + try: + output = get_output(self.ipc_modem('power-on', + '--call=0102030405', + _timeout=timeout)) + except: + raise Exception() + + if output != expected_output: + raise Exception() + + def test_call_without_number(self, timeout=None): + if timeout==None: + timeout = self.timeout + + expected_output = "[E] Missing call number" + output = get_output(self.ipc_modem('power-on', + '--call=', + _timeout=timeout, + _ok_code=SysExit.EX_USAGE.exit_code)) + if output != expected_output: + raise Exception() + + def test_commands(self): + self.test_boot() + self.test_power_on() + self.test_power_off() + self.test_start() + self.test_call_with_number() + self.test_call_without_number() + +def main(): + ipc_modem = IpcModem() + ipc_modem.test_help() + ipc_modem.test_commands() + +if __name__ == '__main__': + rc = main() + sys.exit(rc) diff --git a/tools/nv_data-imei.c b/tools/nv_data-imei.c new file mode 100644 index 0000000..aa62e0c --- /dev/null +++ b/tools/nv_data-imei.c @@ -0,0 +1,1073 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2014 Paul Kocialkowsk <contact@paulk.fr> + * Copyright (C) 2020 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ + +#define _GNU_SOURCE +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <limits.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include <samsung-ipc.h> + +#include "../samsung-ipc/modems/xmm616/xmm616.h" + +#include "nv_data-imei.h" + +#define DEBUG 0 + +#if DEBUG +static int print_offset(struct offset *offset) +{ + printf("offset @ %p: {\n", offset); + printf("\tsize_t offset: 0x%x\n", offset->offset); + printf("\tbool option_set: %s\n", + offset->option_set ? "True" : "False"); + printf("\tint error: %d\n", offset->error); + printf("\tchar *optarg: %s\n", offset->optarg); + printf("}\n"); + + return 0; +} + +static int print_imei(struct imei *imei) +{ + printf("imei @ %p: {\n", imei); + printf("\tchar imei[%d + 1]: %s\n", IMEI_LENGTH, imei->imei); + printf("\tbool option_set: %s\n", imei->option_set ? "True" : "False"); + printf("\tchar *optarg: %s\n", imei->optarg); + printf("}\n"); + + return 0; +} +#endif /* DEBUG */ + +static int list_supported(void) +{ + /* TODO: + * - Print the result in a parsable format (json?) + * - Print the result in and a human format (a table for instance) + * - Add IMEI location (under the battery, unknown, etc) + * - Add IMEI known offsets + */ + printf("Supported devices:\n"); + + /* Offset: 0xE80, other? + * Location: Under the battery + */ + printf("\tNexus S (GT-I902x)\n"); + + return 0; +} + +static int get_offset(const struct command *command, void *arg) +{ + struct offset *offset = arg; + size_t i; + int rc; + bool cmd_has_offset = !!(command->options & OPTION_OFFSET); + bool cmd_requires_offset = !!(command->required_options & + OPTION_OFFSET); + + if (!cmd_has_offset && !offset->option_set) + return 0; + + if (!cmd_has_offset && offset->option_set) { + printf("The %s command doesn't have an -o or --offset option\n", + command->name); + printf("See 'nv_data-imei %s -h' for more details\n", + command->name); + return -EINVAL; + } + + if (cmd_requires_offset && !offset->option_set) { + printf("OFFSET option required\n"); + printf("See nv_data-imei %s -h for more details.\n", + command->name); + return -EINVAL; + } else if (offset->option_set) { + for (i = 0; i < strlen(offset->optarg); i++) { + if (isspace(offset->optarg[i])) { + continue; + } else { + if (offset->optarg[i] == '-') + offset->error |= OFFSET_NEGATIVE; + break; + } + } + + offset->offset = strtoul(offset->optarg, NULL, 0); + rc = errno; + + if (offset->offset == ULONG_MAX && rc == ERANGE) + offset->error |= OFFSET_OVERFLOW; + + if ((offset->error & OFFSET_NEGATIVE) && + (offset->error & OFFSET_OVERFLOW)) + printf("Error: The '%s' offset is negative " + "and too big as well.\n", + offset->optarg); + else if (offset->error & OFFSET_NEGATIVE) + printf("Error: The '%s' offset is negative" + " but offsets cannot be negative.\n", + offset->optarg); + else if (offset->error & OFFSET_OVERFLOW) + printf("Error: The '%s' offset is too big.\n", + offset->optarg); + + if (offset->error) + return -EINVAL; + } + + return 0; +} + +static int get_imei(const struct command *command, void *arg) +{ + struct imei *imei = arg; + + if (command->options & OPTION_IMEI) { + if ((command->required_options & OPTION_IMEI) && + !(imei->option_set)) { + printf("IMEI option required\n"); + printf("See nv_data-imei %s -h for more details.\n", + command->name); + return -EINVAL; + } else if (imei->option_set) { + bool str_is_digit = true; + bool str_len_valid; + size_t len; + size_t i; + + len = strlen(imei->optarg); + str_len_valid = !!(len == IMEI_LENGTH); + + for (i = 0; i < len; i++) { + if (!isdigit(imei->optarg[i])) { + str_is_digit = false; + break; + } + } + + if (!str_is_digit && !str_len_valid) { + printf("The '%s' " + "IMEI is invalid" + " as it does not only contains digits\n", + imei->optarg); + printf("In addition it is also invalid" + " as it is composed of " + "%zd digits instead of %d.\n", + len, IMEI_LENGTH); + return -EINVAL; + } else if (!str_is_digit) { + printf("The '%s' " + "IMEI is invalid" + " as it does not only contains digits\n", + imei->optarg); + return -EINVAL; + } else if (!str_len_valid) { + printf("The '%s' " + "IMEI is invalid as it is composed of " + "%zd digits instead of %d.\n", + imei->optarg, len, IMEI_LENGTH); + return -EINVAL; + } + + /* imei.imei is IMEI_LENGTH + 1 */ + strncpy(imei->imei, imei->optarg, IMEI_LENGTH); + + return 0; + } + } else if (imei->option_set) { + printf("The %s command doesn't have an -i or --imei option\n", + command->name); + printf("See 'nv_data-imei %s -h' for more details\n", + command->name); + return -EINVAL; + } + + return 0; +} + +static const struct command_option commands_options[] = { + { + OPTION_FILE, + "", + "", + "", + NULL + }, + { + OPTION_HELP, + "-h|--help", + "Display the command specific help message", + "-h", + NULL, + }, + { + OPTION_OFFSET, + "-o OFFSET|--offset=OFFSET", + "Use the given OFFSET", + "--offset=0xEC80", + get_offset, + }, + { + OPTION_IMEI, + "-i IMEI|--imei=IMEI", + "Use the given IMEI", + "--imei=355921041234567", + get_imei, + }, + { 0 }, +}; + +static const struct command commands[] = { + { + "list-supported", + "Display supported devices and EFS", + NO_OPTIONS, + NO_OPTIONS, + list_supported, + }, + { + "read-imei", + "Show the current IMEI from nv_data", + OPTION_FILE|OPTION_OFFSET, + OPTION_FILE, + read_imei, + }, + { + "write-imei", + "Store the given IMEI to nv_data (may or may not work)", + OPTION_FILE|OPTION_IMEI|OPTION_OFFSET, + OPTION_FILE|OPTION_IMEI|OPTION_OFFSET, + write_imei, + }, + { + "bruteforce-imei", + "Find the IMEI offset in the nv_data with the given IMEI", + OPTION_FILE|OPTION_IMEI, + OPTION_FILE|OPTION_IMEI, + bruteforce_imei_offset, + }, + { 0 }, +}; + +#if DEBUG +static int print_args(int argc, char *argv[], int optind_index) +{ + int i; + + printf("argc: %d optind: %d ", argc, optind_index); + for (i = 0; i < argc; i++) { + printf("[%d]%s", i, argv[i]); + if (i != (argc - 1)) + printf(" "); + } + printf("\n"); + + return 0; +} +#endif /* DEBUG */ + +static const char warning_msg[] = + "\n" + "+------------------------------------------------------+\n" + "| /!\\ This tool is experimental, use at your own risk |\n" + "+------------------------------------------------------+\n" + "\n" + "It is also dangerous if used improperly: This tool can overwrite any\n" + "part of the nv_data.bin file, without any checks or warnings: If you\n" + "give it any location/offset inside that file, it will proceed\n" + "blindly. This raises several concerns:\n" + "- As we don't know how to recreate valid nv_data.bin files from\n" + " scratch, so you will need to make a backup of it before. We can\n" + " recreate a dummy one, however the result is that it ends up with a\n" + " generic IMEI. This means that most networks will refuse to let you\n" + " register and use their services, which will prevent you from doing\n" + " regular calls / SMS. Changing the IMEI in this generic file has\n" + " not been tested. It's unknown if some other parameters also need\n" + " to be changed to make it work.\n" + "- As we don't know much about the content of the nv_data.bin file,\n" + " it could potentially be dangerous to modify data in the wrong\n" + " location as we don't know the effects. Effects like disrupting the\n" + " telephony network which could in turn prevent people from calling\n" + " medial emergency services cannot be excluded as this file contains\n" + " modem data / parameters and we don't know what they do.\n" + "\n" + "How to use this program.\n" + "- First you need to obtain your current IMEI. This can be done\n" + " through various ways, from looking at the sticker that is written\n" + " under the back cover of your phone, to software means like looking\n" + " in Settings->About Phone->Status->IMEI information in Replicant 6.\n" + "- Then once you have the IMEI you can either use the show-imei\n" + " command and verify that the text you get matches the IMEI you're\n" + " supposed to have, or you can also try to bruteforce the IMEI\n" + " location with the known IMEI.\n" + "- Once this is done you can change the IMEI, and confirm it has been\n" + " changed by looking at\n" + " Settings->About Phone->Status->IMEI information in Replicant 6 or\n" + " through other software means.\n" + "\n" + "In the case where you don't have a valid IMEI, and you are still\n" + "trying to change it (knowing the risks), you will need not to forget\n" + "to verify if it has been changed by looking at\n" + "Settings->About Phone->Status->IMEI information in Replicant 6 or\n" + "through other software means. This will make sure that you only\n" + "modified the offset where the IMEI is really stored and not some\n" + "random offset with potentially crucial modem data.\n" +; + +static void print_warnings(void) +{ + printf("%s", warning_msg); +} + +/* TODO: Enforce type to only allow valid OPTION_* */ +static const struct command_option *get_option(uint8_t given_option) +{ + int i = 0; + + while (true) { + const struct command_option *command_option = + &(commands_options[i++]); + + /* TODO: Get C to do something like if (!option) */ + if (!command_option->option) + break; + + if (command_option->option == given_option) + return command_option; + } + + return NULL; +} + +static int print_all_options(void) +{ + int i = 0; + + while (true) { + const struct command_option *option = &(commands_options[i++]); + + /* TODO: Get C to do something like if (!option) */ + if (!option->option) + break; + + /* Skip options without help like OPTION_FILE */ + if (strlen(option->option_string)) + printf("\t%s # %s\n", option->option_string, + option->help); + } + + return 0; +} + +static int nv_data_imei_help(void) +{ + int i = 0; + + print_warnings(); + printf("\n"); + + printf("Usage:\n"); + printf("\tnv_data-imei FILE COMMAND [OPTIONS]\n"); + printf("\tnv_data-imei COMMAND -h|--help " + "# Display the command specific help message\n"); + + printf("Commands:\n"); + + while (true) { + const struct command *cmd = &(commands[i++]); + + /* TODO: Get C to do something like if (!cmd) */ + if (!cmd->name) + break; + + assert(cmd->name); + assert(cmd->help); + printf("\t%s # %s\n", cmd->name, cmd->help); + } + + printf("Options:\n"); + print_all_options(); + + return 0; +} + +static const struct command *get_command(const char *name) +{ + int i = 0; + + while (true) { + const struct command *cmd = &(commands[i++]); + + /* TODO: Get C to do something like if (!cmd) */ + if (!cmd->name) + break; + + if (strlen(cmd->name) != strlen(name)) + continue; + + if (!strncmp(cmd->name, name, strlen(cmd->name))) + return cmd; + } + + return NULL; + +} + +static int command_help(const char *command_name) +{ + + const struct command *command; + size_t i; + + command = get_command(command_name); + if (!command) + return EX_USAGE; + + printf("Usage:\n"); + + printf("\tnv_data-imei %s%s", + (command->options & OPTION_FILE) ? "FILE " : "", + command->name); + + if (command->options) { + for (i = 0; i < (8 * sizeof(command->options)); i++) { + if (command->options & BIT(i)) { + bool required = !!(command->required_options & + BIT(i)); + const struct command_option *option = + get_option(command->options & BIT(i)); + + /* Check if option and commands are in sync */ + assert(option != NULL); + + if (strlen(option->option_string)) { + if (required) + printf(" <%s>", + option->option_string); + else + printf(" [%s]", + option->option_string); + } + } + } + } + + printf("\n"); + + if (command->options) { + printf("Options:\n"); + for (i = 0; i < (8 * sizeof(command->options)); i++) { + if (command->options & BIT(i)) { + const struct command_option *option = + get_option(command->options & BIT(i)); + + /* Check if option and commands are in sync */ + assert(option != NULL); + + if (strlen(option->option_string)) + printf("\t%s #%s\n", + option->option_string, + option->help); + } + } + } + + printf("Example:\n"); + + printf("\tnv_data-imei %s%s", + (command->options & OPTION_FILE) ? + "./efs_backup_copy/nv_data.bin" : "", + command->name); + + if (command->options) { + for (i = 0; i < (8 * sizeof(command->options)); i++) { + if (command->required_options & BIT(i)) { + const struct command_option *option = + get_option(command->options & BIT(i)); + + /* Check if option and commands are in sync */ + assert(option != NULL); + + printf(" %s", option->example); + } + } + } + + printf("\n"); + + return 0; +} + +static void modem_log_handler(__attribute__((unused)) void *user_data, + const char *msg) +{ + int i, l; + + char *message; + + message = strdup(msg); + l = strlen(message); + + if (l > 1) { + for (i = l ; i > 0 ; i--) { + if (message[i] == '\n') + message[i] = 0; + else if (message[i] != 0) + break; + } + printf("%s\n", message); + } + + free(message); +} + +static int ipc_setup(struct ipc_client **client) +{ + *client = ipc_client_create(IPC_CLIENT_TYPE_DUMMY); + if (*client == NULL) { + printf("Creating client failed\n"); + return -EBADE; + } + + ipc_client_log_callback_register(*client, modem_log_handler, + NULL); + + return 0; +} + +static int decode_imei(unsigned char *buf, struct imei *imei) +{ + int i = 0; + + i += snprintf(imei->imei + i, IMEI_LENGTH + 1 - i, "%01x", + (*buf & 0xf0) >> 4); + + buf += sizeof(unsigned char); + + while (i < IMEI_LENGTH) { + i += snprintf(imei->imei + i, IMEI_LENGTH + 1 - i, + "%02x", + (*buf >> 4) | ((*buf & 0x0f) << 4)); + buf += sizeof(unsigned char); + } + + return 0; +} + +static int encode_imei(unsigned char *buf, struct imei *imei) +{ + int i = 0; + unsigned int v; + int count = 0; + + count = sscanf(imei->imei, "%01x", &v); + if (count != 1) { + printf("%s: first sscanf failed with result: %d\n", + __func__, count); + assert(false); + } + + *buf++ = (v << 4) | 0xA; + + i++; + while (i < IMEI_LENGTH) { + count = sscanf(imei->imei + i, "%02x", &v); + if (count != 1) { + printf("%s: second sscanf failed with result: %d\n", + __func__, count); + assert(false); + } + + *buf++ = v << 4 | ((v & 0xf0) >> 4); + i += 2; + } + + return 0; +} + +int bruteforce_imei_offset(char *nv_data_path, struct imei *given_imei) +{ + struct ipc_client *client = NULL; + size_t file_size; + size_t search_size; + size_t nv_data_chunk_size; + char *buffer = NULL; + char *ptr = NULL; + unsigned char given_imei_buffer[(IMEI_LENGTH + 1) / 2] = { 0 }; + bool found_imei = false; + int rc; + + rc = ipc_setup(&client); + if (rc) + return rc; + + ipc_client_log(client, + "Starting bruteforce\nnv_data_path: %s", + nv_data_path); + + /* The sizes are device dependent, so ipc_client_nv_data_size and + * ipc_client_nv_data_chunk_size were used before. + * However we want the tool to also be able to run on any computer, + * instead of just being able to run on a device. + */ + file_size = file_data_size(client, nv_data_path); + if (file_size == (size_t)-1) { + rc = errno; + goto error; + } + + ipc_client_log(client, "nv_data size: %d\n", file_size); + + /* We only support one device so far */ + nv_data_chunk_size = XMM616_NV_DATA_CHUNK_SIZE; + + buffer = file_data_read(client, nv_data_path, file_size, + nv_data_chunk_size, 0); + + if (buffer == NULL) { + ipc_client_log(client, "Reading nv_data failed"); + rc = -1; + goto error; + } + + rc = encode_imei((unsigned char *)&given_imei_buffer, given_imei); + if (rc < 0) + return rc; + + ptr = buffer; + search_size = file_size; + + do { + ptr = memchr(ptr, given_imei_buffer[0], search_size); + if (ptr) { + if (!strncmp((void*)given_imei_buffer, ptr, + sizeof(given_imei_buffer))) { + ipc_client_log(client, + "=> Found IMEI at 0x%x (%d)", + (ptr - buffer), + (ptr - buffer)); + found_imei = true; + } + + /* Continue searching even if we already found + * it just in case we find the IMEI at a second + * location too. + */ + search_size = file_size - (ptr - buffer); + ptr ++; + } + } while (ptr); + + if (!found_imei) { + rc = 0; + ipc_client_log(client, "=> IMEI not found"); + } + +error: + if (buffer) + free(buffer); + + ipc_client_destroy(client); + + return rc; +} + +int read_imei(char *nv_data_path, struct offset *offset) +{ + struct ipc_client *client = NULL; + struct imei imei; + size_t file_size; + size_t nv_data_chunk_size; + unsigned char *buffer = NULL; + int rc; + + memset(&imei, 0, sizeof(imei)); + + rc = ipc_setup(&client); + if (rc) + return rc; + + /* We only support one device so far */ + file_size = XMM616_NV_DATA_SIZE; + nv_data_chunk_size = XMM616_NV_DATA_CHUNK_SIZE; + + buffer = file_data_read(client, nv_data_path, file_size, + nv_data_chunk_size, 0); + if (buffer == NULL) { + ipc_client_log(client, "Reading nv_data failed\n"); + rc = -1; + goto error; + } + + rc = decode_imei(buffer + offset->offset, &imei); + if (rc) + goto error; + + ipc_client_log(client, "IMEI: %s\n", imei.imei); + + rc = 0; + goto complete; + +error: +complete: + if (buffer) + free(buffer); + + ipc_client_destroy(client); + + return rc; +} + +int write_imei(char *nv_data_path, struct offset *offset, + struct imei *imei) +{ + struct ipc_client *client = NULL; + char *md5_path = NULL; + char *nv_data_secret; + size_t nv_data_chunk_size; + size_t file_size; + char *md5_string = NULL; + unsigned char buffer[(IMEI_LENGTH + 1) / 2] = { 0 }; + size_t length; + int rc; + + rc = ipc_setup(&client); + if (rc) + return rc; + + assert(imei->imei); + assert(strlen(imei->imei) == IMEI_LENGTH); + + rc = asprintf(&md5_path, "%s.md5", nv_data_path); + if (rc == -1) { + ipc_client_log(client, "%s: asprintf failed", __func__); + return -1; + } + + /* We only support one device so far */ + nv_data_secret = XMM616_NV_DATA_SECRET; + file_size = XMM616_NV_DATA_SIZE; + nv_data_chunk_size = XMM616_NV_DATA_CHUNK_SIZE; + + rc = encode_imei((unsigned char *)&buffer, imei); + if (rc < 0) + return rc; + + rc = file_data_write(client, nv_data_path, buffer, sizeof(buffer), + sizeof(buffer), offset->offset); + if (rc == -1) { + rc = errno; + ipc_client_log(client, "Writing nv_data failed\n"); + goto complete; + } + + md5_string = ipc_nv_data_md5_calculate(client, nv_data_path, + nv_data_secret, file_size, + nv_data_chunk_size); + if (md5_string == NULL) { + ipc_client_log(client, "Calculating nv_data md5 failed\n"); + goto error; + } + + length = strlen(md5_string); + + unlink(md5_path); + + rc = file_data_write(client, md5_path, md5_string, length, length, 0); + if (rc == -1) { + rc = errno; + ipc_client_log(client, "Writing nv_data md5 failed\n"); + goto complete; + } + + rc = 0; + goto complete; + +error: + rc = -1; + +complete: + if (md5_path) + free(md5_path); + + ipc_client_destroy(client); + + return rc; +} + +static int errno_to_sysexit(int err) +{ + switch (err) { + case 0: + return EX_OK; + case EACCES: + return EX_NOINPUT; + case -EINVAL: + return EX_USAGE; + default: + printf("%s: error: unknown error code %d.\n", __func__, err); + printf("%s: error code %d needs to be implemented\n", __func__, + err); + assert(false); + } + + return 0; +} + +int main(int argc, char * const argv[]) +{ + opterr = 0; + struct imei imei; + struct offset offset; + const struct command *command = NULL; + const struct command_option *option = NULL; + char *nv_data_path = NULL; + int c, rc; + + memset(&imei, 0, sizeof(imei)); + memset(&offset, 0, sizeof(offset)); + + if (argc == 1) { + printf("Not enough options.\n"); + printf("Try -h to print the help.\n"); + return EX_USAGE; + } + + while (1) { + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"imei", required_argument, 0, 'i' }, + {"offset", required_argument, 0, 'o' }, + {0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "-hi:o:", long_options, NULL); + if (c == -1) + break; + + switch (c) { + case 1: + /* from "man 3 getopt": If the first character of + * optstring is '-', then each nonoption argv-element is + * handled as if it were the argument of an option with + * character code 1. (This is used by programs that were + * written to expect options and other argv-elements in + * any order and that care about the ordering of the + * two.) + */ + + /* nv_data-imei <argument> */ + if (optind == 2 && argc == 2) { + command = get_command(argv[optind - 1]); + /* Example: nv_data-imei list-supported */ + if (command && command->options == NO_OPTIONS) { + rc = command->func(); + return errno_to_sysexit(rc); + } else if (command) { + /* The other commands than + * list-supported need at least a file + * argument so we don't need to handle + * all missing options + */ + if (command->options & OPTION_FILE) { + printf("Error: the '%s' command " + "needs a FILE argument.\n", + argv[optind - 1]); + printf("See 'nv_data-imei %s -h'" + " for more details.\n", + argv[optind - 1]); + } + return EX_USAGE; + } else { + printf("There is no '%s' command\n", + argv[optind - 1]); + printf("Try nv_data-imei -h" + " to print the help.\n"); + return EX_USAGE; + } + /* nv_data-imei FILE COMMAND [...] */ + } else if (optind == 3 && argc >= 3) { + nv_data_path = argv[optind - 2]; + command = get_command(argv[optind - 1]); + if (!command) { + printf("There is no '%s' command\n", + argv[optind - 1]); + printf("Try nv_data-imei -h" + " to print the help.\n"); + return EX_USAGE; + } + /* Some commands don't take arguments nor files. + * The help command is already handled but some + * other command like list-supported need to be + * handled here. + * nv_data-imei FILE list-supported + */ + if (!command->options) { + printf("The '%s' command" + " accepts no options\n", + argv[2]); + printf("Try nv_data-imei %s --help to" + " print the %s command help.\n", + argv[optind - 1], + argv[optind - 1]); + return EX_USAGE; + } + } + break; + case 'h': + /* nv_data-imei -h|--help */ + if (argc == 2) { + return nv_data_imei_help(); + + /* nv_data-imei COMMAND -h|--help */ + } else if (argc == 3) { + rc = command_help(argv[1]); + if (rc) { + printf("There is no '%s' command\n", + argv[1]); + printf("Try nv_data-imei -h" + " to print the help.\n"); + return rc; + } + + /* nv_data-imei FILE COMMAND -h|--help|help is + * not supported because I didn't find an easy + * and robust way to differentiate between + * argv[1] being a file or a command. In other + * words If it was, supported, and that we are + * here, what would be the command? argv[2] or + * argv[3]? How to know for sure that argv[2] is + * really a command and not a file of the same + * name than the command? + */ + + return 0; + } else if (argc > 3) { + printf("Wrong number of arguments." + " Try 'nv_data-imei COMMAND -h' instead" + "\n"); + printf("Example:\n"); + printf("\tnv_data-imei %s -h\n", + commands[0].name); + return EX_USAGE; + } + break; + case 'i': + imei.optarg = optarg; + if (imei.option_set) { + printf("The %s command doesn't have an -i" + " or --imei option\n", + command->name); + printf("See 'nv_data-imei %s -h'" + " for more details\n", + commands->name); + return EX_USAGE; + } + + imei.option_set = true; + + break; + case 'o': + offset.optarg = optarg; + if (offset.option_set) { + printf("The %s command" + " doesn't have an -o or --offset option" + "\n", command->name); + printf("See 'nv_data-imei %s -h'" + " for more details\n", + commands->name); + return EX_USAGE; + } + + offset.option_set = true; + + break; + case '?': + printf("Unknown option '%s'.\n", argv[optind - 1]); + printf("Try nv_data-imei -h to print the help.\n"); + return EX_USAGE; + default: + printf("case '%c':\n", c); + printf("Unknown option '%s'.\n", argv[optind - 1]); + printf("Try nv_data-imei -h to print the help.\n"); + return EX_USAGE; + } + } + + /* We use the - in optstring so all arguments go in the 'case 1:' */ + assert(optind == argc); + + if (argc == 2 || command == NULL) { + /* If none of the commands or options were reached, we are in + * the case where users ran 'nv_data-imei FILE' or used some + * options like -i <argument> without any command. + */ + printf("Missing options, command or invalid command '%s'\n", + argv[1]); + printf("Try -h to print the help.\n"); + return EX_USAGE; + } + + assert(command->options & OPTION_FILE); + assert(command->func); + + + option = get_option(OPTION_IMEI); + rc = option->get_data(command, &imei); + if (rc) + return errno_to_sysexit(rc); + + option = get_option(OPTION_OFFSET); + + rc = option->get_data(command, &offset); + if (rc) + return errno_to_sysexit(rc); + + if (command->options & OPTION_IMEI && + command->options & OPTION_OFFSET) { + rc = command->func(nv_data_path, &offset, &imei); + return errno_to_sysexit(rc); + } + + if (command->options & OPTION_IMEI) { + rc = command->func(nv_data_path, &imei); + return errno_to_sysexit(rc); + } + + if (command->options & OPTION_OFFSET) { + rc = command->func(nv_data_path, &offset); + return errno_to_sysexit(rc); + } + + assert(false); + + return 0; +} diff --git a/tools/nv_data-imei.h b/tools/nv_data-imei.h new file mode 100644 index 0000000..7a153c2 --- /dev/null +++ b/tools/nv_data-imei.h @@ -0,0 +1,82 @@ +/* + * This file is part of libsamsung-ipc. + * + * Copyright (C) 2014 Paul Kocialkowsk <contact@paulk.fr> + * Copyright (C) 2020 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> + * + * libsamsung-ipc is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libsamsung-ipc is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef NV_DATA_IMEI_H +#define NV_DATA_IMEI_H + +#include <stdbool.h> +#include <stdint.h> +#include <string.h> + +#define BIT(n) (1<<n) + +#define IMEI_LENGTH 15 + +extern int opterr; + +/* This enables to enforce the size limit */ +struct imei { + char imei[IMEI_LENGTH + 1]; + bool option_set; + char *optarg; +}; + +#define OFFSET_NEGATIVE BIT(0) +#define OFFSET_OVERFLOW BIT(1) + +struct offset { + size_t offset; + bool option_set; + int error; + char *optarg; +}; + +/* So far we support only one device and IMEI offset, but more offsets do exist + * as the original code from Paul Kocialkowski used the 0xE880 offset instead. + * We don't know yet if multiple offsets can work for one device. + */ +#define DEFAULT_IMEI_OFFSET 0xEC80 + +struct command { + const char *name; + const char *help; + uint8_t options; + uint8_t required_options; + int (*func)(); /* TODO: enfroce argument types */ +}; + +struct command_option { + uint8_t option; + const char *option_string; + const char *help; + const char *example; + int (*get_data)(const struct command *command, void *arg); +}; + +#define NO_OPTIONS 0 +#define OPTION_FILE BIT(0) +#define OPTION_HELP BIT(1) +#define OPTION_IMEI BIT(2) +#define OPTION_OFFSET BIT(3) + +int bruteforce_imei_offset(char *nv_data_path, struct imei *given_imei); +int read_imei(char *nv_data_path, struct offset *offset); +int write_imei(char *nv_data_path, struct offset *offset, struct imei *imei); + +#endif /* NV_DATA_IMEI_H */ diff --git a/tools/nv_data-md5.c b/tools/nv_data-md5.c index e8c9fc8..5edb257 100644 --- a/tools/nv_data-md5.c +++ b/tools/nv_data-md5.c @@ -20,6 +20,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sysexits.h> #include <sys/types.h> @@ -100,4 +101,6 @@ int main(int argc, char *argv[]) error: if (client != NULL) ipc_client_destroy(client); + + return EX_SOFTWARE; } diff --git a/tools/tests/nv_data-imei.py b/tools/tests/nv_data-imei.py new file mode 100755 index 0000000..3447690 --- /dev/null +++ b/tools/tests/nv_data-imei.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +# +# This file is part of libsamsung-ipc. +# +# Copyright (C) 2020 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> +# +# libsamsung-ipc is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# libsamsung-ipc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + +import os +import re +import sys +import sh + +# sysexits.h +class SysExit(object): + #define EX_USAGE 64 /* command line usage error */ + EX_USAGE = sh.ErrorReturnCode_64 + #define EX_NOINPUT 66 /* cannot open input */ + EX_NOINPUT = sh.ErrorReturnCode_66 + +def usage(progname): + print('{} [test]'.format(progname)) + sys.exit(1) + +commands = [ + 'list-supported', + 'read-imei', + 'write-imei', + 'bruteforce-imei', +] + +def get_output(data): + return str(data).replace(os.linesep, '') + +class NvDataImei(object): + def __init__(self): + srcdir = os.environ.get('srcdir', None) + + command_path = '' + if srcdir: + command_path = '.' + os.sep + 'nv_data-imei' + # Enable to run tests without automake + else: + command_path = os.path.dirname(sys.argv[0]) \ + + os.sep \ + + '..' \ + + os.sep \ + + 'nv_data-imei' + + if 'VALGRIND' in os.environ: + self.nv_data_imei = sh.Command(os.environ['VALGRIND']) + self.nv_data_imei = self.nv_data_imei.bake( + '-v', + '--log-file=valgrind.%p.log', + '--leak-check=full', + command_path) + else: + self.nv_data_imei = sh.Command(command_path) + + def test_help(self): + try: + self.nv_data_imei('') + except SysExit.EX_USAGE: + pass + else: + raise Exception() + + for help_arg in ['-h', '--help']: + self.nv_data_imei(help_arg) + for command in commands: + self.nv_data_imei(command, help_arg) + try: + self.nv_data_imei('file', command, help_arg) + except SysExit.EX_USAGE: + pass + else: + raise Exception() + + self.nv_data_imei('list-supported') + + def test_commands(self): + # Create nv_data.bin + valid_imei = '123456789012345' + offset = 0x100 + XMM616_NV_DATA_SIZE = 0x200000 + nv_data_bin = get_output(sh.mktemp()) + sh.ddrescue('/dev/zero', nv_data_bin, '-s', str(XMM616_NV_DATA_SIZE)) + + self.nv_data_imei(nv_data_bin, 'write-imei', '-o', str(hex(offset)), + '-i', valid_imei) + output = get_output(self.nv_data_imei(nv_data_bin, 'read-imei', '-o', + str(hex(offset)))) + print(output) + expect = 'IMEI: ' + valid_imei + if output != expect: + raise Exception() + + output = get_output(self.nv_data_imei(nv_data_bin, 'bruteforce-imei', + '-i', valid_imei)) + print(output) + expect = re.escape('Found IMEI at {} ({})'.format(str(hex(offset)), + offset)) + if not re.search(expect, output): + raise Exception() + + inaccessible_nv_data_bin = str(sh.mktemp('-u')).replace(os.linesep,'') + sh.ddrescue('/dev/zero', inaccessible_nv_data_bin, '-s', + str(XMM616_NV_DATA_SIZE)) + sh.chmod('000', inaccessible_nv_data_bin); + try: + self.nv_data_imei(inaccessible_nv_data_bin, 'write-imei', + '-o', '0x0', '-i', valid_imei) + self.nv_data_imei(inaccessible_nv_data_bin, 'read-imei', + '-o', '0x0') + self.nv_data_imei(inaccessible_nv_data_bin, 'bruteforce-imei', + '-i', valid_imei) + except SysExit.EX_NOINPUT: + pass + else: + raise Exception() + + os.unlink(inaccessible_nv_data_bin) + os.unlink(nv_data_bin) + os.unlink(nv_data_bin + '.md5') + +def main(): + nv_data_imei = NvDataImei() + nv_data_imei.test_help() + nv_data_imei.test_commands() + +if __name__ == '__main__': + rc = main() + sys.exit(rc) diff --git a/tools/tests/nv_data-md5.py b/tools/tests/nv_data-md5.py new file mode 100755 index 0000000..374ded2 --- /dev/null +++ b/tools/tests/nv_data-md5.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# +# This file is part of libsamsung-ipc. +# +# Copyright (C) 2020 Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> +# +# libsamsung-ipc is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# libsamsung-ipc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with libsamsung-ipc. If not, see <http://www.gnu.org/licenses/>. + +import os +import re +import sys +import sh + +def usage(progname): + print('{} [test]'.format(progname)) + sys.exit(1) + +def get_output(data): + return str(data).replace(os.linesep, '') + +class NvDataMD5(object): + def __init__(self): + srcdir = os.environ.get('srcdir', None) + + command_path = '' + if srcdir: + command_path = '.' + os.sep + 'nv_data-md5' + # Enable to run tests without automake + else: + command_path = os.path.dirname(sys.argv[0]) \ + + os.sep \ + + '..' \ + + os.sep \ + + 'nv_data-md5' + + if 'VALGRIND' in os.environ: + self.nv_data_md5 = sh.Command(os.environ['VALGRIND']) + self.nv_data_md5 = self.nv_data_md5.bake( + '-v', + '--log-file=valgrind.%p.log', + '--leak-check=full', + command_path) + else: + self.nv_data_md5 = sh.Command(command_path) + def test_help(self): + try: + self.nv_data_md5() + except sh.ErrorReturnCode_1: + pass + else: + raise Exception() + + def test_commands(self): + expected_md5 = '5293814414abb3831e3fc1a1b35e69bc' + NV_DATA_SIZE = 0x200000 + nv_data_bin = get_output(sh.mktemp()) + + # Create nv_data.bin + sh.ddrescue('/dev/zero', nv_data_bin, '-s', str(NV_DATA_SIZE)) + + output = get_output(self.nv_data_md5(nv_data_bin)) + + print(output) + + if output != expected_md5: + raise Exception() + + os.unlink(nv_data_bin) + +def main(): + nv_data_md5 = NvDataMD5() + nv_data_md5.test_help() + nv_data_md5.test_commands() + +if __name__ == '__main__': + rc = main() + sys.exit(rc) |