aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoic Poulain <loic.poulain@linaro.org>2020-10-29 10:10:25 -0400
committerLoic Poulain <loic.poulain@linaro.org>2020-11-02 10:32:13 -0500
commit0fd482afedf2ef43d8ae792bd9783cf6f718b1d8 (patch)
tree508500fd9d072987c8f926e30445cbfa8a14085e
parent9e1afa9d1d6fd76bc4e07d4eaf066f77e7b74bf5 (diff)
Initial source code commit
Signed-off-by: Loic Poulain <loic.poulain@linaro.org>
-rw-r--r--INSTALL24
-rw-r--r--Makefile27
-rw-r--r--README39
-rw-r--r--mhi-qmi-connect.c868
4 files changed, 958 insertions, 0 deletions
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..165d111
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,24 @@
+0. Download source code
+
+ git clone https://git.linaro.org/landing-teams/working/telit/mhi-qmi-connect.git
+ cd mhi-qmi-connect
+
+1. Pull submodules (libqmi...)
+
+ submodule update --init
+
+2. Build & install libqmi
+
+ cd libqmi
+ ./autogen.sh --enable-qrtr
+ make -j$(nproc)
+ sudo make install
+ cd ..
+
+3. Build & install mhi-qmi-connect
+
+ make
+ sudo make install
+
+
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7eb59b5
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,27 @@
+CC := gcc
+
+MHI_QMI_CONNECT := mhi-qmi-connect
+
+#static="--static"
+CFLAGS := -Wall -g -O2
+CFLAGS += `pkg-config --cflags glib-2.0`
+CFLAGS += `pkg-config --cflags gobject-2.0`
+CFLAGS += `pkg-config --cflags gio-2.0`
+CFLAGS += `pkg-config --cflags qmi-glib`
+LDFLAGS := `pkg-config --libs glib-2.0`
+LDFLAGS += `pkg-config --libs gobject-2.0`
+LDFLAGS += `pkg-config --libs gio-2.0`
+LDFLAGS += `pkg-config --libs qmi-glib`
+#LDFLAGS += -static
+
+SRCS := mhi-qmi-connect.c
+OBJS := $(SRCS:.c=.o)
+
+$(MHI_QMI_CONNECT): $(OBJS)
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+install: $(MHI_QMI_CONNECT)
+ install -D -m 755 $< $(DESTDIR)$(prefix)/bin/$<
+clean:
+ rm -f $(MHI_QMI_CONNECT) $(OBJS)
+
diff --git a/README b/README
new file mode 100644
index 0000000..d7bd991
--- /dev/null
+++ b/README
@@ -0,0 +1,39 @@
+# Connecting to free APN as default internet access (using QRTR/IPCR)
+
+ $ sudo cat /dev/mhi_0000\:02\:00.0_QMI > /dev/null &
+ $ sudo ./mhi-qmi-connect -a free -p 1234 --set-ip --default-route
+ [qrtr://3] Modem is online
+ [qrtr://3] Powering SIM1...
+ [qrtr://3] Verifying SIM1 PIN...
+ [qrtr://3] PIN verified successfully
+ [qrtr://3] Starting Network...
+ [qrtr://3] Network Started!
+ [qrtr://3] IP Family: IPv4
+ [qrtr://3] IPv4 address: 10.188.35.31
+ [qrtr://3] IPv4 primary DNS: 212.27.40.240
+ [qrtr://3] starting rmnet
+ [qrtr://3] <rmnet_data1> interface created
+ [qrtr://3] starting rmnet
+ [qrtr://3] <rmnet_data1> interface IP is 10.188.35.31
+ [qrtr://3] Connected!
+
+Note: for now dummy open of the MHI UCI QMI device is requested
+
+# Connecting to free APN as default internet access (using MHI UCI)
+
+ $ sudo ./mhi-qmi-connect -a free -p 1234 --set-ip --default-route
+ [/dev/mhi_0000:02:00.0_QMI] Modem is online
+ [/dev/mhi_0000:02:00.0_QMI] Powering SIM1...
+ [/dev/mhi_0000:02:00.0_QMI] Verifying SIM1 PIN...
+ [/dev/mhi_0000:02:00.0_QMI] PIN verified successfully
+ [/dev/mhi_0000:02:00.0_QMI] Starting Network...
+ [/dev/mhi_0000:02:00.0_QMI] Network Started!
+ [/dev/mhi_0000:02:00.0_QMI] IP Family: IPv4
+ [/dev/mhi_0000:02:00.0_QMI] IPv4 address: 10.188.35.31
+ [/dev/mhi_0000:02:00.0_QMI] IPv4 primary DNS: 212.27.40.240
+ [/dev/mhi_0000:02:00.0_QMI] starting rmnet
+ [/dev/mhi_0000:02:00.0_QMI] <rmnet_data1> interface created
+ [/dev/mhi_0000:02:00.0_QMI] starting rmnet
+ [/dev/mhi_0000:02:00.0_QMI] <rmnet_data1> interface IP is 10.188.35.31
+ [/dev/mhi_0000:02:00.0_QMI] Connected!
+
diff --git a/mhi-qmi-connect.c b/mhi-qmi-connect.c
new file mode 100644
index 0000000..4c72326
--- /dev/null
+++ b/mhi-qmi-connect.c
@@ -0,0 +1,868 @@
+#include <stdio.h>
+
+#include <arpa/inet.h>
+
+#include <libqmi-glib.h>
+
+#include <glib-unix.h>
+#include <gio/gio.h>
+
+#define MAX_QMAP_DATAGRAM_SIZE 16384
+#define MAX_QMAP_AGGREGATED_DATAGRAM 32
+#define IFACE_ID 4
+#define MUX_ID 1
+#define WWAN_IFACE "mhi_hwip0"
+
+static GMainLoop *loop;
+static GCancellable *cancellable;
+
+static QmiService service_wda = QMI_SERVICE_WDA;
+static QmiService service_wds = QMI_SERVICE_WDS;
+static QmiService service_dms = QMI_SERVICE_DMS;
+static QmiService service_uim = QMI_SERVICE_UIM;
+
+static QmiDevice *device;
+static QmiClientWda *client_wda;
+static QmiClientWds *client_wds;
+static QmiClientDms *client_dms;
+static QmiClientUim *client_uim;
+static QmiWdsConnectionStatus connection_status;
+static guint32 packet_data_handle;
+
+static gchar *apn_str;
+static gchar *sim1_pin_str;
+static gboolean default_route;
+static gboolean set_ip;
+static guint32 mux_id = 1;
+static gchar *qmi_dev;
+
+static gchar ip4_addr[INET_ADDRSTRLEN];
+static gchar ip4_dns[INET_ADDRSTRLEN];
+static gboolean rmnet_setup_done;
+static gchar *ip_route_dump;
+static gboolean ping;
+
+static int exit_code;
+
+/* 16 - cleanup/exit operations */
+
+static void release_client_ready(QmiDevice *dev, GAsyncResult *res)
+{
+ return;
+}
+
+static void release_qmi_clients(void)
+{
+ if (QMI_IS_CLIENT(client_wda))
+ qmi_device_release_client(device, (QmiClient *)client_wda, 0, 5, cancellable,
+ (GAsyncReadyCallback) release_client_ready, NULL);
+ if (QMI_IS_CLIENT(client_uim))
+ qmi_device_release_client(device, (QmiClient *)client_uim, 0, 5, cancellable,
+ (GAsyncReadyCallback) release_client_ready, NULL);
+ if (QMI_IS_CLIENT(client_dms))
+ qmi_device_release_client(device, (QmiClient *)client_dms, 0, 5, cancellable,
+ (GAsyncReadyCallback) release_client_ready, NULL);
+ if (QMI_IS_CLIENT(client_wds))
+ qmi_device_release_client(device, (QmiClient *)client_wds, 0, 5, cancellable,
+ (GAsyncReadyCallback) release_client_ready, NULL);
+}
+
+static void stop_rmnet(void)
+{
+ GError *error = NULL;
+ gchar cmdline[1000] = {0};
+
+ if (!rmnet_setup_done)
+ return;
+
+ g_print("[%s] stopping rmnet\n", qmi_device_get_path_display(device));
+
+ /* Set RMNET interface down */
+ g_snprintf(cmdline, sizeof(cmdline), "ip link set rmnet_data%u down", mux_id);
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ /* Set WWAN interface down */
+ g_snprintf(cmdline, sizeof(cmdline), "ip link set %s down", WWAN_IFACE);
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ /* del RMNET interface */
+ g_snprintf(cmdline, sizeof(cmdline), "ip link delete rmnet_data%u", mux_id);
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ if (!default_route)
+ return;
+
+ g_snprintf(cmdline, sizeof(cmdline), "ip route del default");
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ /* TODO: restore routing (default route) */
+}
+
+static void stop_network_ready(QmiClientWds *client, GAsyncResult *res)
+{
+ GError *error = NULL;
+ QmiMessageWdsStopNetworkOutput *output;
+
+ output = qmi_client_wds_stop_network_finish(client, res, &error);
+ if (!output) {
+ g_printerr("error: operation failed: %s\n", error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ if (!qmi_message_wds_stop_network_output_get_result (output, &error)) {
+ g_printerr("error: couldn't stop network: %s\n", error->message);
+ g_error_free(error);
+ qmi_message_wds_stop_network_output_unref(output);
+ release_qmi_clients(); return;
+ }
+ qmi_message_wds_stop_network_output_unref(output);
+
+ g_print("[%s] Network stopped\n", qmi_device_get_path_display(device));
+
+ /* TODO ? reconnect automatically ? */
+ exit(EXIT_SUCCESS);
+}
+
+static void stop_network(void)
+{
+ QmiMessageWdsStopNetworkInput *input;
+
+ input = qmi_message_wds_stop_network_input_new ();
+ qmi_message_wds_stop_network_input_set_packet_data_handle(input, packet_data_handle, NULL);
+ qmi_client_wds_stop_network(client_wds, input, 10, cancellable,
+ (GAsyncReadyCallback)stop_network_ready, NULL);
+ qmi_message_wds_stop_network_input_unref(input);
+}
+
+/* 15 - network status tracking */
+static void get_packet_service_status_ready(QmiClientWds *client, GAsyncResult *res)
+{
+ QmiMessageWdsGetPacketServiceStatusOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_wds_get_packet_service_status_finish(client, res, &error);
+ if (!output) {
+ g_printerr("error: operation failed: %s\n", error->message);
+ g_error_free(error);
+ release_qmi_clients(); release_qmi_clients(); return;
+ }
+
+ if (!qmi_message_wds_get_packet_service_status_output_get_result(output, &error)) {
+ g_printerr("error: couldn't get packet service status: %s\n", error->message);
+ g_error_free(error);
+ qmi_message_wds_get_packet_service_status_output_unref(output);
+ release_qmi_clients(); release_qmi_clients(); return;
+ }
+
+ qmi_message_wds_get_packet_service_status_output_get_connection_status(output, &connection_status, NULL);
+ qmi_message_wds_get_packet_service_status_output_unref(output);
+
+ if (connection_status != QMI_WDS_CONNECTION_STATUS_CONNECTED) {
+ g_print("[%s] Connection status: '%s'\n", qmi_device_get_path_display(device),
+ qmi_wds_connection_status_get_string(connection_status));
+ stop_rmnet();
+ stop_network();
+ return;
+ }
+
+ if (!ping) {
+ g_print("\r[%s] Connected!\n", qmi_device_get_path_display(device));
+ ping = !ping;
+ }
+}
+
+static gboolean start_network_status(gpointer user_data)
+{
+ if (connection_status != QMI_WDS_CONNECTION_STATUS_CONNECTED)
+ return FALSE;
+
+ qmi_client_wds_get_packet_service_status(client_wds, NULL, 10, cancellable,
+ (GAsyncReadyCallback)get_packet_service_status_ready,
+ NULL);
+
+ return TRUE;
+}
+
+/* 14 - Configure interfaces TODO: move that part out of this tool */
+static void setup_rmnet(void)
+{
+ GError *error = NULL;
+ gchar cmdline[1000] = {0};
+
+ /* Clean-up */
+ g_print("[%s] starting rmnet\n", qmi_device_get_path_display(device));
+
+ /* Set wwan MTU */
+ g_snprintf(cmdline, sizeof(cmdline), "ip link set %s mtu %u", WWAN_IFACE,
+ MAX_QMAP_DATAGRAM_SIZE);
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ /* Add link */
+ g_snprintf(cmdline, sizeof(cmdline), "ip link add link %s name rmnet_data%u type rmnet mux_id %u",
+ WWAN_IFACE, mux_id, mux_id);
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+ g_print("[%s] <rmnet_data%u> interface created\n", qmi_device_get_path_display(device), mux_id);
+
+ if (set_ip) {
+ g_print("[%s] starting rmnet\n", qmi_device_get_path_display(device));
+
+ /* Set RMNET IP address */
+ g_snprintf(cmdline, sizeof(cmdline), "ip addr add %s/27 dev rmnet_data%u",
+ ip4_addr, mux_id);
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+ g_print("[%s] <rmnet_data%u> interface IP is %s\n",
+ qmi_device_get_path_display(device), mux_id, ip4_addr);
+ }
+
+ /* Set RMNET MTU */
+ g_snprintf(cmdline, sizeof(cmdline), "ip link set rmnet_data%u mtu %u",
+ mux_id, MAX_QMAP_DATAGRAM_SIZE);
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ /* Set WWAN interface up */
+ g_snprintf(cmdline, sizeof(cmdline), "ip link set %s up", WWAN_IFACE);
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ /* Set RMNET interface up */
+ g_snprintf(cmdline, sizeof(cmdline), "ip link set rmnet_data%u up", mux_id);
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ rmnet_setup_done = TRUE;
+
+ if (!default_route || !set_ip)
+ return;
+
+ /* Save routing */
+ g_snprintf(cmdline, sizeof(cmdline), "ip route save");
+ if (!g_spawn_command_line_sync(cmdline, &ip_route_dump, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ /* Set RMNET as default route */
+ g_snprintf(cmdline, sizeof(cmdline), "ip route replace default via %s", ip4_addr);
+ if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &error)) {
+ g_printerr("%s: %s\n", cmdline, error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ /* TODO: DNS */
+}
+
+/* 13 - Network settings retrieved */
+static void get_current_settings_ready(QmiClientWds *client, GAsyncResult *res)
+{
+ QmiWdsIpFamily ip_family = QMI_WDS_IP_FAMILY_UNSPECIFIED;
+ QmiMessageWdsGetCurrentSettingsOutput *output;
+ GError *error = NULL;
+ guint32 addr = 0;
+ struct in_addr in_addr_val;
+
+ output = qmi_client_wds_get_current_settings_finish (client, res, &error);
+ if (!output) {
+ g_printerr("error: operation failed: %s\n", error->message);
+ g_error_free (error);
+ release_qmi_clients(); return;
+ }
+
+ if (!qmi_message_wds_get_current_settings_output_get_result (output, &error)) {
+ g_printerr("error: couldn't get current settings: %s\n", error->message);
+ g_error_free (error);
+ qmi_message_wds_get_current_settings_output_unref (output);
+ release_qmi_clients(); return;
+ }
+
+ if (qmi_message_wds_get_current_settings_output_get_ip_family (output, &ip_family, NULL))
+ g_print("[%s] IP Family: %s\n", qmi_device_get_path_display(device),
+ ((ip_family == QMI_WDS_IP_FAMILY_IPV4) ? "IPv4" :
+ ((ip_family == QMI_WDS_IP_FAMILY_IPV6) ? "IPv6" :
+ "unknown")));
+
+ /* TODO */
+ if (ip_family == QMI_WDS_IP_FAMILY_IPV6) {
+ g_printerr("IPv6 not supported\n");
+ release_qmi_clients(); return;
+ }
+
+ if (qmi_message_wds_get_current_settings_output_get_ipv4_address(output, &addr, NULL)) {
+ in_addr_val.s_addr = GUINT32_TO_BE (addr);
+ memset (ip4_addr, 0, sizeof (ip4_addr));
+ inet_ntop (AF_INET, &in_addr_val, ip4_addr, sizeof (ip4_addr));
+ g_print("[%s] IPv4 address: %s\n", qmi_device_get_path_display(device), ip4_addr);
+ }
+
+ if (qmi_message_wds_get_current_settings_output_get_primary_ipv4_dns_address(output, &addr, NULL)) {
+ in_addr_val.s_addr = GUINT32_TO_BE (addr);
+ memset (ip4_dns, 0, sizeof (ip4_dns));
+ inet_ntop (AF_INET, &in_addr_val, ip4_dns, sizeof (ip4_dns));
+ g_print ("[%s] IPv4 primary DNS: %s\n", qmi_device_get_path_display(device), ip4_dns);
+ }
+
+ qmi_message_wds_get_current_settings_output_unref(output);
+
+ setup_rmnet();
+}
+
+/* 12 - Network started, get network settings */
+static gboolean start_network(gpointer user_data);
+
+static void start_network_ready(QmiClientWds *client, GAsyncResult *res)
+{
+ QmiMessageWdsGetCurrentSettingsInput *input;
+ QmiMessageWdsStartNetworkOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_wds_start_network_finish(client, res, &error);
+ if (!output) {
+ g_printerr("error: operation failed: %s\n", error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ if (!qmi_message_wds_start_network_output_get_result(output, &error)) {
+ if (error->code == QMI_PROTOCOL_ERROR_CALL_FAILED) {
+ g_printerr("error: Call failed, retrying in 5 seconds\n");
+ g_timeout_add(5000, start_network, NULL);
+ g_error_free(error);
+ return;
+ }
+
+ g_printerr("error: couldn't start network: %s\n", error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ qmi_message_wds_start_network_output_get_packet_data_handle(output, &packet_data_handle, NULL);
+ qmi_message_wds_start_network_output_unref(output);
+
+ g_print("[%s] Network Started!\n", qmi_device_get_path_display(device));
+
+ input = qmi_message_wds_get_current_settings_input_new();
+ qmi_message_wds_get_current_settings_input_set_requested_settings(input,
+ (QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DNS_ADDRESS |
+ QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GRANTED_QOS |
+ QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_ADDRESS |
+ QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_GATEWAY_INFO |
+ QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_MTU |
+ QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DOMAIN_NAME_LIST |
+ QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_IP_FAMILY),
+ NULL);
+
+ qmi_client_wds_get_current_settings(client, input, 10, cancellable,
+ (GAsyncReadyCallback)get_current_settings_ready, NULL);
+ qmi_message_wds_get_current_settings_input_unref(input);
+
+ /* track network status */
+ connection_status = QMI_WDS_CONNECTION_STATUS_CONNECTED;
+ g_timeout_add(5000, start_network_status, NULL);
+}
+
+/* 11 - Start Network */
+static gboolean start_network(gpointer user_data)
+{
+ QmiMessageWdsStartNetworkInput *input;
+
+ g_print("[%s] Starting Network...\n", qmi_device_get_path_display(device));
+
+ input = qmi_message_wds_start_network_input_new();
+
+ qmi_message_wds_start_network_input_set_apn(input, apn_str, NULL);
+
+ qmi_client_wds_start_network(client_wds, input, 45, cancellable,
+ (GAsyncReadyCallback)start_network_ready, NULL);
+
+ qmi_message_wds_start_network_input_unref(input);
+
+ return FALSE;
+}
+
+/* 10 - Bind mux data port done, start network */
+static void bind_mux_data_port_ready(QmiClientWds *client, GAsyncResult *res)
+{
+ QmiMessageWdsBindMuxDataPortOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_wds_bind_mux_data_port_finish (client, res, &error);
+ if (!output) {
+ g_printerr ("error: operation failed: %s\n", error->message);
+ g_error_free (error);
+ release_qmi_clients(); return;
+ }
+
+ if (!qmi_message_wds_bind_mux_data_port_output_get_result (output, &error)) {
+ g_printerr ("error: couldn't bind mux data port: %s\n", error->message);
+ g_error_free (error);
+ qmi_message_wds_bind_mux_data_port_output_unref(output);
+ release_qmi_clients(); return;
+ }
+
+ qmi_message_wds_bind_mux_data_port_output_unref(output);
+
+ /* Add delay before starting network, to be sure we have carrier */
+ /* TODO: check connection state instead */
+ g_timeout_add(1000, start_network, NULL);
+}
+
+/* 9 - WDS client ready, Bind mux data port (mux-id) */
+static void allocate_wds_client_ready(QmiDevice *dev, GAsyncResult *res)
+{
+ QmiMessageWdsBindMuxDataPortInput *input = NULL;
+ GError *error = NULL;
+
+ client_wds = (QmiClientWds *)qmi_device_allocate_client_finish(dev, res, &error);
+ if (!client_wda) {
+ g_printerr ("error: couldn't create client for the '%s' service: %s\n",
+ "WDA", error->message);
+ release_qmi_clients(); return;
+ }
+
+ input = qmi_message_wds_bind_mux_data_port_input_new();
+
+ qmi_message_wds_bind_mux_data_port_input_set_endpoint_info(
+ input, QMI_DATA_ENDPOINT_TYPE_PCIE, IFACE_ID, &error);
+ qmi_message_wds_bind_mux_data_port_input_set_mux_id(
+ input, MUX_ID, &error);
+
+ qmi_client_wds_bind_mux_data_port(client_wds, input, 10, cancellable,
+ (GAsyncReadyCallback) bind_mux_data_port_ready, NULL);
+
+ qmi_message_wds_bind_mux_data_port_input_unref(input);
+}
+
+/* 8 - Data format set, start WDS operations */
+static void set_data_format_ready (QmiClientWda *client, GAsyncResult *res)
+{
+ QmiMessageWdaSetDataFormatOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_wda_set_data_format_finish(client_wda, res, &error);
+ if (!output) {
+ g_printerr("error: operation failed: %s\n", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ if (!qmi_message_wda_set_data_format_output_get_result (output, &error)) {
+ g_printerr ("error: couldn't set data format: %s\n", error->message);
+ g_error_free (error);
+ qmi_message_wda_set_data_format_output_unref(output);
+ return;
+ }
+
+ qmi_device_allocate_client(device, service_wds, QMI_CID_NONE, 10, cancellable,
+ (GAsyncReadyCallback)allocate_wds_client_ready, NULL);
+}
+
+/* 7 - WDA client ready, Set data format */
+static void allocate_wda_client_ready(QmiDevice *dev, GAsyncResult *res)
+{
+ QmiMessageWdaSetDataFormatInput *input = NULL;
+ GError *error = NULL;
+
+ client_wda = (QmiClientWda *)qmi_device_allocate_client_finish(dev, res, &error);
+ if (!client_wda) {
+ g_printerr ("error: couldn't create client for the '%s' service: %s\n",
+ "WDA", error->message);
+ release_qmi_clients(); return;
+ }
+
+ input = qmi_message_wda_set_data_format_input_new();
+
+ /* QMAP aggregation protocol */
+ qmi_message_wda_set_data_format_input_set_uplink_data_aggregation_protocol(
+ input, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAP, &error);
+ qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_protocol(
+ input, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAP, &error);
+ qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_max_size(
+ input, MAX_QMAP_AGGREGATED_DATAGRAM, &error);
+ qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_max_datagrams(
+ input, MAX_QMAP_DATAGRAM_SIZE, &error);
+ qmi_message_wda_set_data_format_input_set_link_layer_protocol(
+ input, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, &error);
+ qmi_message_wda_set_data_format_input_set_endpoint_info(
+ input, QMI_DATA_ENDPOINT_TYPE_PCIE, IFACE_ID, &error);
+
+ qmi_client_wda_set_data_format(client_wda, input, 10, cancellable,
+ (GAsyncReadyCallback)set_data_format_ready, NULL);
+}
+
+/* 6.3 - SIM PIN verified, start WDA operations */
+static void verify_pin_ready(QmiClientUim *client, GAsyncResult *res)
+{
+ QmiMessageUimVerifyPinOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_uim_verify_pin_finish(client, res, &error);
+ if (!output) {
+ g_printerr ("error: operation failed: %s\n", error->message);
+ g_error_free(error);
+ release_qmi_clients(); return;
+ }
+
+ if (!qmi_message_uim_verify_pin_output_get_result (output, &error)) {
+ guint8 verify_retries_left;
+ guint8 unblock_retries_left;
+
+ g_printerr("error: couldn't verify PIN: %s\n", error->message);
+ g_error_free (error);
+
+ if (qmi_message_uim_verify_pin_output_get_retries_remaining (
+ output,
+ &verify_retries_left,
+ &unblock_retries_left,
+ NULL)) {
+ g_printerr("[%s] Retries left:\n"
+ "\tVerify: %u\n"
+ "\tUnblock: %u\n",
+ qmi_device_get_path_display(device),
+ verify_retries_left,
+ unblock_retries_left);
+ }
+
+ qmi_message_uim_verify_pin_output_unref(output);
+ release_qmi_clients(); return;
+ }
+
+ g_print("[%s] PIN verified successfully\n", qmi_device_get_path_display(device));
+
+ qmi_message_uim_verify_pin_output_unref(output);
+
+ qmi_device_allocate_client(device, service_wda, QMI_CID_NONE, 10, cancellable,
+ (GAsyncReadyCallback)allocate_wda_client_ready, NULL);
+}
+
+/* 6.2 - SIM powered on, Set pin code */
+static void power_on_sim_ready(QmiClientUim *client, GAsyncResult *res)
+{
+ QmiMessageUimVerifyPinInput *input = NULL;
+ QmiMessageUimPowerOnSimOutput *output;
+ GError *error = NULL;
+ GArray *dummy_aid;
+
+ output = qmi_client_uim_power_on_sim_finish(client_uim, res, &error);
+ if (!output) {
+ g_printerr("error: operation failed: %s\n", error->message);
+ g_error_free (error);
+ release_qmi_clients(); return;
+ }
+
+ if (!qmi_message_uim_power_on_sim_output_get_result(output, &error) &&
+ error->code != QMI_PROTOCOL_ERROR_NO_EFFECT) {
+ g_printerr("error: could not power on SIM: %s\n", error->message);
+ g_error_free(error);
+ qmi_message_uim_power_on_sim_output_unref(output);
+ release_qmi_clients(); return;
+ }
+
+ qmi_message_uim_power_on_sim_output_unref(output);
+
+ g_print("[%s] Verifying SIM1 PIN...\n", qmi_device_get_path_display(device));
+
+ input = qmi_message_uim_verify_pin_input_new();
+
+ dummy_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
+
+ qmi_message_uim_verify_pin_input_set_info(input, 1, sim1_pin_str, &error);
+ qmi_message_uim_verify_pin_input_set_session (input, QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
+ dummy_aid, /* ignored */ &error);
+
+ qmi_client_uim_verify_pin(client, input, 10, cancellable,
+ (GAsyncReadyCallback)verify_pin_ready, NULL);
+
+ g_array_unref(dummy_aid);
+ qmi_message_uim_verify_pin_input_unref(input);
+}
+
+/* 6.1 - UIM client ready, power-on SIM */
+static void allocate_uim_client_ready(QmiDevice *dev, GAsyncResult *res)
+{
+ QmiMessageUimPowerOnSimInput *input;
+ GError *error = NULL;
+ guint slot = 1;
+
+ client_uim = (QmiClientUim *)qmi_device_allocate_client_finish(dev, res, &error);
+ if (!client_uim) {
+ g_printerr ("error: couldn't create client for the '%s' service: %s\n",
+ "WDA", error->message);
+ release_qmi_clients(); return;
+ }
+
+ g_print("[%s] Powering SIM1...\n", qmi_device_get_path_display(device));
+
+ input = qmi_message_uim_power_on_sim_input_new();
+
+ qmi_message_uim_power_on_sim_input_set_slot(input, slot, &error);
+
+ qmi_client_uim_power_on_sim(client_uim, input, 10, cancellable,
+ (GAsyncReadyCallback)power_on_sim_ready, NULL);
+
+ qmi_message_uim_power_on_sim_input_unref(input);
+}
+
+/* 6 - Operating mode retrieved, start WDA operations */
+static void get_operating_mode_ready(QmiClientDms *client, GAsyncResult *res)
+{
+ QmiMessageDmsGetOperatingModeOutput *output;
+ QmiDmsOperatingMode mode;
+ GError *error = NULL;
+
+ output = qmi_client_dms_get_operating_mode_finish(client, res, &error);
+ if (!output) {
+ g_printerr ("error: operation failed: %s\n", error->message);
+ g_error_free (error);
+ release_qmi_clients(); return;
+ }
+
+ if (!qmi_message_dms_get_operating_mode_output_get_result(output, &error)) {
+ g_printerr ("error: couldn't get power state: %s\n", error->message);
+ g_error_free (error);
+ qmi_message_dms_get_operating_mode_output_unref(output);
+ release_qmi_clients(); return;
+ }
+
+ qmi_message_dms_get_operating_mode_output_get_mode(output, &mode, NULL);
+
+ if (mode != QMI_DMS_OPERATING_MODE_ONLINE) {
+ /* TODO put it online */
+ g_print("[%s] Modem is offline\n", qmi_device_get_path_display(device));
+ release_qmi_clients(); return;
+ }
+
+ g_print("[%s] Modem is online\n", qmi_device_get_path_display(device));
+
+ if (sim1_pin_str) {
+ qmi_device_allocate_client(device, service_uim, QMI_CID_NONE, 10, cancellable,
+ (GAsyncReadyCallback)allocate_uim_client_ready, NULL);
+ } else {
+ qmi_device_allocate_client(device, service_wda, QMI_CID_NONE, 10, cancellable,
+ (GAsyncReadyCallback)allocate_wda_client_ready, NULL);
+ }
+}
+
+/* 5 - DMS client ready, Request current operating mode */
+static void allocate_dms_client_ready(QmiDevice *dev, GAsyncResult *res)
+{
+ GError *error = NULL;
+
+ client_dms = (QmiClientDms *)qmi_device_allocate_client_finish(dev, res, &error);
+ if (!client_dms) {
+ g_printerr ("error: couldn't create client for the '%s' service: %s\n",
+ "DMS", error->message);
+ exit(EXIT_FAILURE);
+ }
+
+ qmi_client_dms_get_operating_mode(client_dms, NULL, 10, cancellable,
+ (GAsyncReadyCallback)get_operating_mode_ready, NULL);
+}
+
+/* 4 - QMI device opened, Start DMS operations */
+static void device_open_ready(QmiDevice *dev, GAsyncResult *res)
+{
+ GError *error = NULL;
+
+ if (!qmi_device_open_finish(dev, res, &error)) {
+ g_printerr("error: couldn't open the QmiDevice: %s\n", error->message);
+ exit(EXIT_FAILURE);
+ }
+
+ qmi_device_allocate_client(dev, service_dms, QMI_CID_NONE, 10, cancellable,
+ (GAsyncReadyCallback)allocate_dms_client_ready, NULL);
+}
+
+/* 3 - Device created, Open QMI device */
+static void device_new_ready(GObject *unused, GAsyncResult *res)
+{
+ QmiDeviceOpenFlags open_flags = QMI_DEVICE_OPEN_FLAGS_NONE;
+ GError *error = NULL;
+
+ device = qmi_device_new_finish(res, &error);
+ if (!device) {
+ g_printerr("error: couldn't create QmiDevice: %s\n", error->message);
+ exit(EXIT_FAILURE);
+ }
+
+ qmi_device_open(device, open_flags, 15, cancellable,
+ (GAsyncReadyCallback)device_open_ready, NULL);
+}
+
+/* 2 - Services found, let's create a QMI device from the QRTR node */
+static void wait_for_services_ready(QrtrNode *node, GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+
+ if (!qrtr_node_wait_for_services_finish (node, res, &error)) {
+ g_printerr ("error: failed to wait for QRTR services: %s\n", error->message);
+ exit(EXIT_FAILURE);
+ }
+
+ qmi_device_new_from_node(node, cancellable, (GAsyncReadyCallback)device_new_ready, NULL);
+}
+
+/* 1.1 - QRTR node found, Wait for QMI services we need */
+static void qrtr_node_ready(GObject *unused, GAsyncResult *res)
+{
+ QrtrNode *node;
+ GError *error = NULL;
+ GArray *services;
+
+ node = qrtr_node_for_id_finish(res, &error);
+ if (!node) {
+ g_printerr("error: couldn't open QRTR node: %s\n", error->message);
+ exit(EXIT_FAILURE);
+ }
+
+ services = g_array_sized_new(FALSE, FALSE, sizeof(QmiService), 10);
+ g_array_append_val(services, service_wda);
+ g_array_append_val(services, service_wds);
+ g_array_append_val(services, service_dms);
+
+ qrtr_node_wait_for_services(node, services, 5, NULL,
+ (GAsyncReadyCallback) wait_for_services_ready, NULL);
+
+ g_array_unref(services);
+}
+
+/* X - Signals */
+static gboolean signals_handler(void)
+{
+ /* TODO perform some nice cleanup here */
+ stop_rmnet();
+ release_qmi_clients();
+
+ if (loop && g_main_loop_is_running(loop)) {
+ g_printerr("cancelling the main loop...\n");
+ g_idle_add((GSourceFunc)g_main_loop_quit, loop);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static GOptionEntry main_entries[] = {
+ { "apn", 'a', 0, G_OPTION_ARG_STRING, &apn_str,
+ "Specify device APN name", "[APN]"
+ },
+ { "sim1-pin", 'p', 0, G_OPTION_ARG_STRING, &sim1_pin_str,
+ "Specify SIM1 pin code", "[SIM1-PIN]"
+ },
+ { "device", 'd', 0, G_OPTION_ARG_STRING, &qmi_dev,
+ "Use passed MHI QMI device (instead of IPCR)", NULL
+ },
+ { "mux-id", 0, 0, G_OPTION_ARG_INT, &mux_id,
+ "QMAP/RMNET mux-id of the connection (default is 1)", NULL
+ },
+ { "set-ip", 0, 0, G_OPTION_ARG_NONE, &set_ip,
+ "Setup rmnet IP address and DNS from Bearer context info", NULL
+ },
+ { "default-route", 0, 0, G_OPTION_ARG_NONE, &default_route,
+ "Set rmnet interface as the defaut network route", NULL
+ },
+ { NULL }
+};
+
+int main(int argc, char **argv)
+{
+ GOptionContext *context;
+ GError *error = NULL;
+ GFile *qmi_file;
+
+ context = g_option_context_new("");
+
+ g_option_context_add_main_entries(context, main_entries, NULL);
+ if (!g_option_context_parse(context, &argc, &argv, &error)) {
+ g_printerr ("error: %s\n", error->message);
+ exit(EXIT_FAILURE);
+ }
+ g_option_context_free(context);
+
+ if (!apn_str) {
+ g_printerr("error: no APN name\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (default_route && !set_ip) {
+ g_printerr("error: Cannot set --default-route without --set-ip option\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (qmi_dev) { /* Use MHI QMI character device */
+ gchar *id;
+
+ qmi_file = g_file_new_for_commandline_arg(qmi_dev);
+ id = g_file_get_path(qmi_file);
+ qmi_device_new(qmi_file, cancellable, (GAsyncReadyCallback)device_new_ready, NULL);
+ g_free(id);
+ } else { /* QMI services over IPCR/qrtr */
+ /* TODO: auto detect qrtr node via lookup */
+ /* For now qrtr modem services are always exposed via node 3 */
+ qrtr_node_for_id(3, 10, cancellable, (GAsyncReadyCallback)qrtr_node_ready, NULL);
+ }
+
+ /* Connect signals */
+ g_unix_signal_add(SIGINT, (GSourceFunc) signals_handler, NULL);
+ g_unix_signal_add(SIGHUP, (GSourceFunc) signals_handler, NULL);
+ g_unix_signal_add(SIGTERM, (GSourceFunc) signals_handler, NULL);
+
+ exit_code = 0;
+ loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(loop);
+
+ if (cancellable)
+ g_object_unref(cancellable);
+ if (client_wda)
+ g_object_unref(client_wda);
+ if (client_wds)
+ g_object_unref(client_wds);
+ if (client_dms)
+ g_object_unref(client_dms);
+ if (client_uim)
+ g_object_unref(client_uim);
+ if (device)
+ g_object_unref(device);
+
+ g_main_loop_unref(loop);
+
+ return exit_code;
+}