diff options
author | Loic Poulain <loic.poulain@linaro.org> | 2020-10-29 10:10:25 -0400 |
---|---|---|
committer | Loic Poulain <loic.poulain@linaro.org> | 2020-11-02 10:32:13 -0500 |
commit | 0fd482afedf2ef43d8ae792bd9783cf6f718b1d8 (patch) | |
tree | 508500fd9d072987c8f926e30445cbfa8a14085e | |
parent | 9e1afa9d1d6fd76bc4e07d4eaf066f77e7b74bf5 (diff) |
Initial source code commit
Signed-off-by: Loic Poulain <loic.poulain@linaro.org>
-rw-r--r-- | INSTALL | 24 | ||||
-rw-r--r-- | Makefile | 27 | ||||
-rw-r--r-- | README | 39 | ||||
-rw-r--r-- | mhi-qmi-connect.c | 868 |
4 files changed, 958 insertions, 0 deletions
@@ -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) + @@ -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; +} |