aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDenis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>2021-01-21 10:47:36 +0100
committerDenis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>2021-03-01 16:37:34 +0100
commitd5328e57286080ecb3924e913338d4d07b1d3f1f (patch)
treea7c2eddcbb34bb92385b24c31fb02af72b6bea18
parent08b0c4868a5cdcc1973cb7fdc6c62f0a88a7a262 (diff)
downloadhardware_replicant_libsamsung-ipc-d5328e57286080ecb3924e913338d4d07b1d3f1f.tar.gz
hardware_replicant_libsamsung-ipc-d5328e57286080ecb3924e913338d4d07b1d3f1f.tar.bz2
hardware_replicant_libsamsung-ipc-d5328e57286080ecb3924e913338d4d07b1d3f1f.zip
[wip] tools: add ipc-imei
The idea here is to use that to validate that libsamsung-ipc works fine by reading some information of the modem data or the SIM card through libsamsung-ipc. The code is written in a way that enables code sharing as much as possible to be able to build other custom tests or tools for other usages (like a PCSC interface to the SIM card for instance). In the long run, the idea would be to be able to write very simple applications without the huge boilerplate that is needed to handle the modem and concentrate on the specific information the application needs to send and receive. A very simple application could for instance look like that: /* A free softrware license */ /* Some includes */ /* callbacks for logging */ static int my_callback(struct ipc_client *client, struct ipc_message *resp, void* data) { /* User code */ } static do_something(struct ipc_client *client) { /* User code */ } int main(int argc, char** argv) { [...] client = ipc_client_create(IPC_CLIENT_TYPE_FMT); if (client == NULL) { printf("Creating client failed\n"); return 1; } rc = init_modem(&client, STATE_LPM) if (rc < 0) return rc; register_callback(&my_callback, NULL); do_something(&client); return 0; } The boilerplate would then be provided in another C file and/or linked to from a small library. +======+ | TODO | +======+ The tool-name in this commit message need to be changed from ipc-sim to ipc-imei as that was done in the code already. On Replicant 6, we still have a segfault when exiting though: [D] ipc-sim: modem_read_loop done [D] modem_stop: ipc_client_close [D] modem_stop: ipc_client_power_off [D] modem_stop: Modem stopped [1] + Segmentation fault ipc-sim In addition, we still need to fix the startup code as it segfaults on Replicant 6 about 50% of the time: [D] modem_start: requested state MODEM_STATE_LPM: 1 [D] Starting i9300 modem boot [D] Opened modem image device [D] Mapped modem image data to memory [D] Opened modem boot device [D] Opened modem link device [D] Turned the modem off [D] Turned the modem on [D] Waited for link connected [D] Wrote ATAT in ASCII [D] Reading boot ACK failed [D] Sending XMM626 HSIC PSI failed [D] Booting failed: error -1 [1] + Stopped (signal) ipc-sim Signed-off-by: Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org> Fix tool name Signed-off-by: Denis 'GNUtoo' Carikli <GNUtoo@cyberdimension.org>
-rw-r--r--Android.mk16
-rw-r--r--tools/Makefile.am5
-rw-r--r--tools/common/modem.c413
-rw-r--r--tools/common/modem.h62
-rw-r--r--tools/ipc-imei.c213
5 files changed, 709 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
index 3b08853..574a452 100644
--- a/Android.mk
+++ b/Android.mk
@@ -175,6 +175,22 @@ LOCAL_SHARED_LIBRARIES := libsamsung-ipc
include $(BUILD_EXECUTABLE)
+##################
+# ipc-imei tool #
+##################
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/android_versions.mk
+
+LOCAL_MODULE := ipc-imei
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := tools/ipc-imei.c tools/common/modem.c
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/tools/include/glibc
+LOCAL_SHARED_LIBRARIES := libsamsung-ipc
+
+include $(BUILD_EXECUTABLE)
+
#################
# ipc-test tool #
#################
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 4d5fa8b..c11f648 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -6,6 +6,7 @@ AM_CFLAGS = \
bin_PROGRAMS = \
ipc-modem \
+ ipc-imei \
ipc-test \
nv_data-imei \
nv_data-md5 \
@@ -22,6 +23,10 @@ ipc_modem_SOURCES = ipc-modem.c
ipc_modem_LDADD = $(top_builddir)/samsung-ipc/libsamsung-ipc.la
ipc_modem_LDFLAGS =
+ipc_imei_SOURCES = ipc-imei.c common/modem.c
+ipc_imei_LDADD = $(top_builddir)/samsung-ipc/libsamsung-ipc.la
+ipc_imei_LDFLAGS =
+
ipc_test_SOURCES = ipc-test.c
ipc_test_LDADD = $(top_builddir)/samsung-ipc/libsamsung-ipc.la
ipc_test_LDFLAGS =
diff --git a/tools/common/modem.c b/tools/common/modem.c
new file mode 100644
index 0000000..fac9797
--- /dev/null
+++ b/tools/common/modem.c
@@ -0,0 +1,413 @@
+/*
+ * This file is part of libsamsung-ipc.
+ *
+ * Copyright (C) 2014 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 <fcntl.h>
+#include <stdlib.h> /* system("dmesg") */
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "modem.h"
+
+int seq;
+
+static enum modem_state current_state = MODEM_STATE_LPM;
+
+static enum modem_callback_state callback_state = MODEM_CALLBACK_STATE_UTILS;
+
+/* Taken from tools/ipc-modem.c */
+int seq_get(void)
+{
+ if (seq == 0xff)
+ seq = 0x00;
+
+ seq++;
+
+ return seq;
+}
+
+static int modem_response_sec(struct ipc_client *client,
+ struct ipc_message *resp,
+ enum modem_state new_state)
+{
+ struct ipc_sec_pin_status_response_data *sim_status;
+ unsigned char type;
+
+ if (!client)
+ return 0;
+
+
+ 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_client_log(client, "[I] SIM card not found\n");
+
+ if (new_state == MODEM_STATE_SIM_OK)
+ return -ENODEV; /* SIM not found but required */
+ else
+ return 0; /* Modem init done */
+ case IPC_SEC_PIN_STATUS_LOCK_SC:
+ /* TODO */
+ switch (sim_status->facility_lock) {
+ case IPC_SEC_FACILITY_LOCK_TYPE_SC_PIN1_REQ:
+ ipc_client_log(client,
+ "[I] "
+ "The card is locked with the PIN1\n"
+ "TODO: implement unlocking the card"
+ );
+ break;
+ case IPC_SEC_FACILITY_LOCK_TYPE_SC_PUK_REQ:
+ ipc_client_log(client,
+ "[I] "
+ "The card is locked with the PUK\n"
+ "TODO: implement unlocking the card"
+ );
+ break;
+ case IPC_SEC_FACILITY_LOCK_TYPE_SC_CARD_BLOCKED:
+ ipc_client_log(client, "[I] "
+ "The SIM Card is blocked:\n"
+ "Unless you have a "
+ "programable card with the "
+ "ADM1 pin you might need to "
+ "contact your operator.\n");
+ break;
+ }
+
+ /* Only fail if the SIM card is required */
+ if (new_state == MODEM_STATE_SIM_OK)
+ return -EACCES;
+
+ break;
+ case IPC_SEC_PIN_STATUS_INIT_COMPLETE:
+ ipc_client_log(client, "[3] SIM init complete\n");
+ if (current_state == MODEM_STATE_NORMAL) {
+ current_state = MODEM_STATE_SIM_OK;
+ /* In any case we're done when the SIM is
+ * ready
+ */
+ return 0;
+ }
+ break;
+ case IPC_SEC_PIN_STATUS_PB_INIT_COMPLETE:
+ ipc_client_log(client,
+ "[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:
+ ipc_client_log(client,
+ "[I] No SIM card type: unknown (absent?)"
+ "\n");
+
+ /* Only fail if the SIM card is required */
+ if (new_state == MODEM_STATE_SIM_OK)
+ return -ENODEV;
+ break;
+ case IPC_SEC_SIM_CARD_TYPE_SIM:
+ case IPC_SEC_SIM_CARD_TYPE_USIM:
+ ipc_client_log(client, "[I] SIM card found\n");
+ break;
+ }
+ break;
+ }
+
+ return -EAGAIN;
+}
+
+int modem_stop(struct ipc_client *client)
+{
+ int rc;
+
+ ipc_client_log(client, "%s: ipc_client_close", __FUNCTION__);
+ rc = ipc_client_close(client);
+ if (rc < 0) {
+ ipc_client_log(client, "Closing failed: error %d\n", rc);
+ return rc;
+ }
+
+ ipc_client_log(client, "%s: ipc_client_power_off", __FUNCTION__);
+ rc = ipc_client_power_off(client);
+ if (rc < 0) {
+ ipc_client_log(client, "Powering on failed: error %d\n", rc);
+ return rc;
+ }
+
+ ipc_client_log(client, "%s: Modem stopped", __FUNCTION__);
+
+ return 0;
+}
+
+/* TODO: share the code */
+static int modem_response_pwr(struct ipc_client *client,
+ struct ipc_message *resp,
+ __attribute__((unused)) enum modem_state new_state)
+{
+ int state_n;
+
+ if (!client)
+ return 0;
+
+ switch (resp->command) {
+ case IPC_PWR_PHONE_PWR_UP:
+ ipc_client_log(client, "[2] Phone is powered up (LPM)!\n");
+ current_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_client_log(client, "Power state is now: NORMAL\n");
+ break;
+ case IPC_PWR_PHONE_STATE_LPM:
+ ipc_client_log(client, "Power state is now: "
+ "LPM (Low Power Mode)?\n");
+ break;
+ }
+#else
+ ipc_client_log(client, "Power state is now: 0x%x\n",
+ current_state);
+#endif
+ current_state = state_n;
+ break;
+
+ }
+
+ return 0;
+}
+
+/*
+ * -EAGAIN is returned when modem_response_handle handled a
+ * response and another value < 0 when there were errors.
+ * It returns 0 when it's done initializing the modem, so
+ * application writers can more easily run the code they
+ * whish at this point and handle the responses the way they
+ * whish.
+ */
+static int modem_start_response_handle(struct ipc_client *client,
+ struct ipc_message *resp,
+ enum modem_state new_state)
+{
+ int rc;
+
+ if (!client)
+ return 0;
+
+ switch (IPC_GROUP(resp->command)) {
+ case IPC_GROUP_PWR:
+ rc = modem_response_pwr(client, resp, new_state);
+ if (rc == 0)
+ return -EAGAIN;
+ return rc;
+ case IPC_GROUP_SEC:
+ return modem_response_sec(client, resp, new_state);
+ default:
+ ipc_client_log(client, "Unhandled %s command",
+ ipc_group_string(IPC_GROUP(resp->command)));
+ return -EAGAIN;
+ }
+}
+
+int register_app_modem_response_handler(
+ struct ipc_client *client,
+ struct app_modem_response_handler *handler,
+ int (*handler_func)(struct ipc_client *client, struct ipc_message *resp, void *handler_data),
+ void *handler_func_data)
+{
+ if (!client)
+ return 0;
+
+ if (!handler)
+ return 0;
+
+ handler->handler = handler_func;
+ handler->data = handler_func_data;
+
+ return 0;
+}
+
+static int modem_response_handle(struct ipc_client *client,
+ struct ipc_message *resp,
+ enum modem_state new_state,
+ struct app_modem_response_handler *handler)
+{
+ int rc;
+
+ if (!client)
+ return 0;
+
+
+ if (callback_state == MODEM_CALLBACK_STATE_UTILS)
+ rc = modem_start_response_handle(client, resp, new_state);
+ else
+ rc = handler->handler(client, resp, handler->data);
+
+ return rc;
+}
+
+/* TODO: share modem_read_loop and fix it elsewhere too */
+/* TODO: new_state is not needed for the application */
+int modem_read_loop(struct ipc_client *client,
+ enum modem_state new_state,
+ struct app_modem_response_handler *handler)
+{
+ struct ipc_message resp;
+ int rc;
+
+ if (!client)
+ return 0;
+
+ 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) {
+ if (resp.data != NULL)
+ free(resp.data);
+ return rc;
+ }
+
+ rc = modem_response_handle(client, &resp, new_state, handler);
+
+ ipc_client_log(client, "modem_response_handle: rc=%d", rc);
+
+ if (resp.data != NULL)
+ free(resp.data);
+
+ if (rc == -EAGAIN) {
+ continue;
+ } else if (rc < 0) {
+ return rc;
+ } else if (rc == 0) {
+ /* The callback exited normally because it reached
+ * new_state.
+ * It's now to the app callback to take over
+ */
+ if (callback_state == MODEM_CALLBACK_STATE_UTILS)
+ callback_state = MODEM_CALLBACK_STATE_APP;
+ return 0;
+ }
+ }
+}
+
+static int _modem_start(struct ipc_client *client)
+{
+ int rc;
+
+ if (!client)
+ return 0;
+
+ rc = ipc_client_data_create(client);
+ if (rc < 0) {
+ ipc_client_log(client, "Creating data failed: error %d\n", rc);
+ return rc;
+ }
+
+ rc = ipc_client_boot(client);
+ if (rc < 0) {
+ ipc_client_log(client, "Booting failed: error %d\n", rc);
+ return rc;
+ }
+
+ rc = ipc_client_power_on(client);
+ if (rc < 0) {
+ ipc_client_log(client, "Powering on failed: error %d\n", rc);
+ return rc;
+ }
+
+ rc = ipc_client_open(client);
+ if (rc < 0) {
+ ipc_client_log(client, "ipc_client_open failed: error %d\n",
+ rc);
+ return rc;
+ }
+
+ rc = ipc_client_power_on(client);
+ if (rc < 0) {
+ ipc_client_log(client,
+ "ipc_client_power_on failed: error %d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+const char *modem_state_to_string(enum modem_state state)
+{
+ static char group_string[5] = { 0 };
+
+ switch (state) {
+ case MODEM_STATE_LPM:
+ return "MODEM_STATE_LPM";
+ case MODEM_STATE_NORMAL:
+ return "MODEM_STATE_NORMAL";
+ case MODEM_STATE_SIM_OK:
+ return "MODEM_STATE_SIM_OK";
+ default:
+ snprintf((char *) &group_string, sizeof(group_string), "0x%02x",
+ (unsigned int)group_string);
+ return group_string;
+ }
+};
+
+int modem_start(struct ipc_client *client, enum modem_state new_state,
+ struct app_modem_response_handler *handler)
+{
+ int rc;
+
+ if (!client)
+ return 0;
+
+ ipc_client_log(client, "%s: requested state %s: %d\n",
+ __FUNCTION__,
+ modem_state_to_string(new_state),
+ new_state);
+
+ rc = _modem_start(client);
+ if (rc < 0) {
+ ipc_client_destroy(client);
+ return 1;
+ }
+
+ rc = modem_read_loop(client, new_state, handler);
+ if (rc < 0)
+ ipc_client_log(client,
+ "modem_read_loop failed: error %d\n", rc);
+ return 0;
+}
diff --git a/tools/common/modem.h b/tools/common/modem.h
new file mode 100644
index 0000000..0075c3b
--- /dev/null
+++ b/tools/common/modem.h
@@ -0,0 +1,62 @@
+/*
+ * This file is part of libsamsung-ipc.
+ *
+ * Copyright (C) 2014 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/>.
+ */
+
+#ifndef TOOLS_UTILS_H
+#define TOOLS_UTILS_H
+
+#include <stddef.h> /*fixme */
+#include <samsung-ipc.h>
+
+#define BIT(n) (1<<n)
+
+int seq_get(void);
+
+struct app_modem_response_handler {
+ int (*handler)(struct ipc_client *client, struct ipc_message *resp, void *handler_data);
+ void *data;
+};
+
+enum modem_state {
+ MODEM_STATE_LPM = BIT(0),
+ MODEM_STATE_NORMAL = BIT(1),
+ MODEM_STATE_SIM_OK = BIT(2),
+};
+
+enum modem_callback_state {
+ MODEM_CALLBACK_STATE_UTILS = BIT(0),
+ MODEM_CALLBACK_STATE_APP = BIT(1),
+};
+
+int modem_start(struct ipc_client *client, enum modem_state state,
+ struct app_modem_response_handler *handler);
+int modem_stop(struct ipc_client *client);
+
+int modem_read_loop(struct ipc_client *client,
+ enum modem_state new_state,
+ struct app_modem_response_handler *handler);
+
+int register_app_modem_response_handler(
+ struct ipc_client *client,
+ struct app_modem_response_handler *handler,
+ int (*handler_func)(struct ipc_client *client, struct ipc_message *resp, void *handler_data),
+ void *handler_func_data);
+
+
+#endif /* TOOLS_UTILS_H */
diff --git a/tools/ipc-imei.c b/tools/ipc-imei.c
new file mode 100644
index 0000000..004efa1
--- /dev/null
+++ b/tools/ipc-imei.c
@@ -0,0 +1,213 @@
+/*
+ * This file is part of libsamsung-ipc.
+ *
+ * Copyright (C) 2014 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 <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "common/modem.h"
+
+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);
+}
+
+static void usage(char* progname)
+{
+ printf("Usage: %s\n", progname);
+}
+
+int ipc_imei_request_imei(struct ipc_client *client)
+{
+ int rc;
+
+ if (!client)
+ return 0;
+
+ struct ipc_misc_me_sn_request_data request_data;
+
+ request_data.type = IPC_MISC_ME_SN_SERIAL_NUM;
+
+ rc = ipc_client_send(client, seq_get(), IPC_MISC_ME_SN, IPC_TYPE_GET,
+ (void *) &request_data, sizeof(request_data));
+ if (rc < 0)
+ ipc_client_log(client, "ipc_client_send failed with error %d\n",
+ rc);
+
+ return rc;
+}
+
+int ipc_imei_parse_imei_response(struct ipc_client *client,
+ struct ipc_message *message,
+ __attribute__((unused)) void* app_data)
+{
+ struct ipc_misc_me_sn_response_data *data;
+ char *imei;
+
+ if (!client)
+ return 0;
+
+ if (message == NULL) {
+ ipc_client_log(client,
+ "%s: ipc_message is null\n", __FUNCTION__);
+ return -EAGAIN;
+ }
+
+ data = (struct ipc_misc_me_sn_response_data *) message->data;
+
+ if (data->type != IPC_MISC_ME_SN_SERIAL_NUM)
+ return -EAGAIN;
+
+ imei = ipc_misc_me_sn_extract(data);
+
+ if (strlen (imei) > 15 * sizeof(char))
+ imei[15] = '\0';
+
+ ipc_client_log(client, "%s: found IMEI '%s'\n", __FUNCTION__, imei);
+
+ free(imei);
+
+ /* Exit */
+ return 0;
+}
+
+static int ipc_imei_response_handle(struct ipc_client *client,
+ struct ipc_message *resp,
+ void *data)
+{
+ int rc;
+
+ if (!client)
+ return 0;
+
+ switch (IPC_GROUP(resp->command)) {
+ case IPC_GROUP_MISC:
+ rc = ipc_imei_parse_imei_response(client, resp, data);
+ return rc;
+ default:
+ ipc_client_log(client, "Unhandled %s command",
+ ipc_group_string(IPC_GROUP(resp->command)));
+ return -EAGAIN;
+ }
+}
+
+
+int main(int argc, char** argv)
+{
+ struct ipc_client *client = NULL;
+ struct app_modem_response_handler handler;
+
+ int fd;
+ int rc;
+
+ if (argc == 0) {
+ exit(EX_USAGE);
+ } else if (argc != 1) {
+ usage(argv[0]);
+ exit(EX_USAGE);
+ }
+
+ client = ipc_client_create(IPC_CLIENT_TYPE_FMT);
+ if (client == NULL) {
+ printf("Creating client failed\n");
+ return 1;
+ }
+
+ fd = open("/dev/tty0", O_WRONLY|O_NOCTTY);
+ if (fd == -1) {
+ fd = errno;
+ printf("Opening /dev/tty0 failed: error %d: %s\n",
+ fd, strerror(fd));
+ return -1;
+ }
+
+ rc = ipc_client_log_callback_register(client, modem_log_handler, NULL);
+ if (rc < 0) {
+ printf("ipc_client_log_callback_registerma failed:"
+ " error %d\n", rc);
+ return 1;
+ }
+
+ /* register new callback that would take over after modem_start is
+ * done
+ */
+ rc = register_app_modem_response_handler(client, &handler,
+ ipc_imei_response_handle,
+ NULL);
+ if (rc < 0) {
+ ipc_client_log(client,
+ "register_app_modem_response_handler failed: "
+ "error %d\n",
+ rc);
+ return 1;
+ }
+
+ rc = modem_start(client, MODEM_STATE_LPM, &handler);
+ if (rc < 0) {
+ ipc_client_log(client, "modem_start failed: error %d\n", rc);
+ return 1;
+ }
+
+ ipc_client_log(client, "modem_start done\n");
+
+ ipc_imei_request_imei(client);
+
+ ipc_client_log(client, "ipc_imei_request_imei done\n");
+
+ /* TODO: MODEM_STATE_LPM is not needed here */
+ rc = modem_read_loop(client, MODEM_STATE_LPM, &handler);
+ if (rc < 0)
+ ipc_client_log(client,
+ "ipc-imei: modem_read_loop failed: error %d\n",
+ rc);
+
+ ipc_client_log(client, "ipc-imei: modem_read_loop done");
+
+ rc = modem_stop(client);
+ if (rc < 0) {
+ ipc_client_log(client, "modem_stop failed: error %d\n", rc);
+ return 1;
+ }
+
+ return 0;
+}