umbim: add home provider query support
[oweals/umbim.git] / cli.c
diff --git a/cli.c b/cli.c
index 328804d292c3ea8f2219ef5e87c1652028db7eb9..3a845d463d843040e19325de2098831ca47fb014 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -1,3 +1,17 @@
+/*
+ * umbim
+ * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * 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 General Public License for more details.
+ */
+
 #define __STDC_FORMAT_MACROS
 #include <inttypes.h>
 
@@ -10,6 +24,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <ctype.h>
 
 #include <libubox/utils.h>
 #include <libubox/uloop.h>
@@ -27,7 +42,7 @@ static int _argc;
 static char **_argv;
 
 static int
-mbim_device_caps_response(void *buffer, int len)
+mbim_device_caps_response(void *buffer, size_t len)
 {
        struct mbim_basic_connect_device_caps_r *caps = (struct mbim_basic_connect_device_caps_r *) buffer;
        char *deviceid, *firmwareinfo, *hardwareinfo;
@@ -59,7 +74,7 @@ mbim_device_caps_response(void *buffer, int len)
 }
 
 static int
-mbim_pin_state_response(void *buffer, int len)
+mbim_pin_state_response(void *buffer, size_t len)
 {
        struct mbim_basic_connect_pin_r *pin = (struct mbim_basic_connect_pin_r *) buffer;
 
@@ -81,7 +96,33 @@ mbim_pin_state_response(void *buffer, int len)
 }
 
 static int
-mbim_registration_response(void *buffer, int len)
+mbim_home_provider_response(void *buffer, size_t len)
+{
+       struct mbim_basic_connect_home_provider_r *state = (struct mbim_basic_connect_home_provider_r *) buffer;
+       struct mbimprovider *provider;
+       char *provider_id, *provider_name;
+
+       if (len < sizeof(struct mbim_basic_connect_home_provider_r)) {
+               fprintf(stderr, "message not long enough\n");
+               return -1;
+       }
+
+       provider = &state->provider;
+       provider_id = mbim_get_string(&provider->providerid, buffer);
+       provider_name = mbim_get_string(&provider->providername, buffer);
+
+       printf("  provider_id: %s\n", provider_id);
+       printf("  provider_name: %s\n", provider_name);
+       printf("  cellularclass: %04X - %s\n", le32toh(provider->cellularclass),
+               mbim_enum_string(mbim_cellular_class_values, le32toh(provider->cellularclass)));
+       printf("  rssi: %04X\n", le32toh(provider->rssi));
+       printf("  errorrate: %04X\n", le32toh(provider->errorrate));
+
+       return 0;
+}
+
+static int
+mbim_registration_response(void *buffer, size_t len)
 {
        struct mbim_basic_connect_register_state_r *state = (struct mbim_basic_connect_register_state_r *) buffer;
        char *provider_id, *provider_name, *roamingtext;
@@ -116,11 +157,11 @@ mbim_registration_response(void *buffer, int len)
 }
 
 static int
-mbim_subscriber_response(void *buffer, int len)
+mbim_subscriber_response(void *buffer, size_t len)
 {
        struct mbim_basic_connect_subscriber_ready_status_r *state = (struct mbim_basic_connect_subscriber_ready_status_r *) buffer;
        char *subscriberid, *simiccid;
-       int nr;
+       unsigned int nr;
 
        if (len < sizeof(struct mbim_basic_connect_subscriber_ready_status_r)) {
                fprintf(stderr, "message not long enough\n");
@@ -137,7 +178,7 @@ mbim_subscriber_response(void *buffer, int len)
        if (le32toh(state->readyinfo) & MBIM_READY_INFO_FLAG_PROTECT_UNIQUE_ID)
                printf("  dont display subscriberID: 1\n");
        for (nr = 0; nr < le32toh(state->telephonenumberscount); nr++) {
-               struct mbim_string *str = buffer + le32toh(state->telephonenumbers) + (nr * sizeof(struct mbim_string));
+               struct mbim_string *str = (void *)&state->telephonenumbers + (nr * sizeof(struct mbim_string));
                char *number = mbim_get_string(str, buffer);
                printf("  number: %s\n", number);
        }
@@ -149,7 +190,7 @@ mbim_subscriber_response(void *buffer, int len)
 }
 
 static int
-mbim_attach_response(void *buffer, int len)
+mbim_attach_response(void *buffer, size_t len)
 {
        struct mbim_basic_connect_packet_service_r *ps = (struct mbim_basic_connect_packet_service_r *) buffer;
 
@@ -162,8 +203,8 @@ mbim_attach_response(void *buffer, int len)
                mbim_enum_string(mbim_nw_error_values, le32toh(ps->nwerror)));
        printf("  packetservicestate: %04X - %s\n", le32toh(ps->packetservicestate),
                mbim_enum_string(mbim_packet_service_state_values, le32toh(ps->packetservicestate)));
-       printf("  uplinkspeed: %"PRIu64"\n", le64toh(ps->uplinkspeed));
-       printf("  downlinkspeed: %"PRIu64"\n", le64toh(ps->downlinkspeed));
+       printf("  uplinkspeed: %"PRIu64"\n", (uint64_t) le64toh(ps->uplinkspeed));
+       printf("  downlinkspeed: %"PRIu64"\n", (uint64_t) le64toh(ps->downlinkspeed));
 
        if (MBIM_PACKET_SERVICE_STATE_ATTACHED == le32toh(ps->packetservicestate))
                return 0;
@@ -172,7 +213,7 @@ mbim_attach_response(void *buffer, int len)
 }
 
 static int
-mbim_connect_response(void *buffer, int len)
+mbim_connect_response(void *buffer, size_t len)
 {
        struct mbim_basic_connect_connect_r *c = (struct mbim_basic_connect_connect_r *) buffer;
 
@@ -198,11 +239,12 @@ mbim_connect_response(void *buffer, int len)
 }
 
 static int
-mbim_config_response(void *buffer, int len)
+mbim_config_response(void *buffer, size_t len)
 {
        struct mbim_basic_connect_ip_configuration_r *ip = (struct mbim_basic_connect_ip_configuration_r *) buffer;
-       char ipv4[16];
-       int i;
+       char out[40];
+       unsigned int i;
+       uint32_t offset;
 
        if (len < sizeof(struct mbim_basic_connect_ip_configuration_r)) {
                fprintf(stderr, "message not long enough\n");
@@ -211,26 +253,57 @@ mbim_config_response(void *buffer, int len)
 
        if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS)
                for (i = 0; i < le32toh(ip->ipv4addresscount); i++) {
-                       mbim_get_ipv4(buffer, ipv4, ip->ipv4address + (i * 4));
-                       printf("  ipv4address: %s\n", ipv4);
+                       offset = le32toh(ip->ipv4address) + (i * 4);
+                       mbim_get_ipv4(buffer, out, 4 + offset);
+                       printf("  ipv4address: %s/%d\n", out, mbim_get_int(buffer, offset));
                }
        if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) {
-               mbim_get_ipv4(buffer, ipv4, ip->ipv4gateway);
-               printf("  ipv4gateway: %s\n", ipv4);
+               mbim_get_ipv4(buffer, out, le32toh(ip->ipv4gateway));
+               printf("  ipv4gateway: %s\n", out);
        }
        if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
                printf("  ipv4mtu: %d\n", le32toh(ip->ipv4mtu));
        if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS)
                for (i = 0; i < le32toh(ip->ipv4dnsservercount); i++) {
-                       mbim_get_ipv4(buffer, ipv4, ip->ipv4dnsserver + (i * 4));
-                       printf("  ipv4dnsserver: %s\n", ipv4);
+                       mbim_get_ipv4(buffer, out, le32toh(ip->ipv4dnsserver) + (i * 4));
+                       printf("  ipv4dnsserver: %s\n", out);
                }
 
-       printf("  ipv6configurationavailable: %04X\n", le32toh(ip->ipv6configurationavailable));
+       if (le32toh(ip->ipv6configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS)
+               for (i = 0; i < le32toh(ip->ipv6addresscount); i++) {
+                       offset = le32toh(ip->ipv6address) + (i * 16);
+                       mbim_get_ipv6(buffer, out, 4 + offset);
+                       printf("  ipv6address: %s/%d\n", out, mbim_get_int(buffer, offset));
+               }
+       if (le32toh(ip->ipv6configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) {
+               mbim_get_ipv6(buffer, out, le32toh(ip->ipv6gateway));
+               printf("  ipv6gateway: %s\n", out);
+       }
+       if (le32toh(ip->ipv6configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
+               printf("  ipv6mtu: %d\n", le32toh(ip->ipv6mtu));
+       if (le32toh(ip->ipv6configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS)
+               for (i = 0; i < le32toh(ip->ipv6dnsservercount); i++) {
+                       mbim_get_ipv6(buffer, out, le32toh(ip->ipv6dnsserver) + (i * 16));
+                       printf("  ipv6dnsserver: %s\n", out);
+               }
 
        return 0;
 }
 
+static int
+mbim_radio_response(void *buffer, size_t len)
+{
+       struct mbim_basic_connect_radio_state_r *r = (struct mbim_basic_connect_radio_state_r *) buffer;
+
+       if (len < sizeof(struct mbim_basic_connect_radio_state_r)) {
+               fprintf(stderr, "message not long enough\n");
+               return -1;
+       }
+       printf("  hwradiostate: %s\n", r->hwradiostate ? "on" : "off");
+       printf("  swradiostate: %s\n", r->swradiostate ? "on" : "off");
+       return 0;
+}
+
 static int
 mbim_device_caps_request(void)
 {
@@ -247,10 +320,27 @@ mbim_pin_state_request(void)
        return mbim_send_command_msg();
 }
 
+static int
+mbim_home_provider_request(void)
+{
+       mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CID_BASIC_CONNECT_HOME_PROVIDER, 0);
+
+       return mbim_send_command_msg();
+}
+
 static int
 mbim_registration_request(void)
 {
-       mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_REGISTER_STATE, 0);
+       if (_argc > 0) {
+               struct mbim_basic_connect_register_state_s *rs =
+                       (struct mbim_basic_connect_register_state_s *) mbim_setup_command_msg(basic_connect,
+                                       MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_REGISTER_STATE,
+                                       sizeof(struct mbim_basic_connect_register_state_s));
+
+               rs->registeraction = htole32(MBIM_REGISTER_ACTION_AUTOMATIC);
+       } else {
+               mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_REGISTER_STATE, 0);
+       }
 
        return mbim_send_command_msg();
 }
@@ -291,6 +381,7 @@ mbim_detach_request(void)
 static int
 mbim_connect_request(void)
 {
+       char *apn;
        struct mbim_basic_connect_connect_s *c =
                (struct mbim_basic_connect_connect_s *) mbim_setup_command_msg(basic_connect,
                        MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_CONNECT,
@@ -299,9 +390,35 @@ mbim_connect_request(void)
        c->activationcommand = htole32(MBIM_ACTIVATION_COMMAND_ACTIVATE);
        c->iptype = htole32(MBIM_CONTEXT_IP_TYPE_DEFAULT);
        memcpy(c->contexttype, uuid_context_type_internet, 16);
-       if (_argc > 0)
-               mbim_encode_string(&c->accessstring, *_argv);
-
+       if (_argc > 0) {
+               apn = index(*_argv, ':');
+               if (!apn) {
+                       apn = *_argv;
+               } else {
+                       apn[0] = 0;
+                       apn++;
+                       if (!strcmp(*_argv, "ipv4"))
+                               c->iptype = htole32(MBIM_CONTEXT_IP_TYPE_IPV4);
+                       else if (!strcmp(*_argv, "ipv6"))
+                               c->iptype = htole32(MBIM_CONTEXT_IP_TYPE_IPV6);
+                       else if (!strcmp(*_argv, "ipv4v6"))
+                               c->iptype = htole32(MBIM_CONTEXT_IP_TYPE_IPV4V6);
+               }
+               mbim_encode_string(&c->accessstring, apn);
+       }
+       if (_argc > 3) {
+               if (!strcmp(_argv[1], "pap"))
+                       c->authprotocol = htole32(MBIM_AUTH_PROTOCOL_PAP);
+               else if (!strcmp(_argv[1], "chap"))
+                       c->authprotocol = htole32(MBIM_AUTH_PROTOCOL_CHAP);
+               else if (!strcmp(_argv[1], "mschapv2"))
+                       c->authprotocol = htole32(MBIM_AUTH_PROTOCOL_MSCHAPV2);
+
+               if (c->authprotocol) {
+                       mbim_encode_string(&c->username, _argv[2]);
+                       mbim_encode_string(&c->password, _argv[3]);
+               }
+       }
        return mbim_send_command_msg();
 }
 
@@ -321,6 +438,24 @@ mbim_disconnect_request(void)
        return mbim_send_command_msg();
 }
 
+static char*
+mbim_pin_sanitize(char *pin)
+{
+       char *p;
+
+       while (*pin && !isdigit(*pin))
+               pin++;
+       p = pin;
+       if (!*p)
+               return NULL;
+       while (*pin && isdigit(*pin))
+               pin++;
+       if (*pin)
+               *pin = '\0';
+
+       return p;
+}
+
 static int
 mbim_pin_unlock_request(void)
 {
@@ -328,6 +463,12 @@ mbim_pin_unlock_request(void)
                (struct mbim_basic_connect_pin_s *) mbim_setup_command_msg(basic_connect,
                        MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_PIN,
                        sizeof(struct mbim_basic_connect_pin_s));
+       char *pin = mbim_pin_sanitize(_argv[0]);
+
+       if (!pin || !strlen(pin)) {
+               fprintf(stderr, "failed to sanitize the pincode\n");
+               return -1;
+       }
 
        p->pintype = htole32(MBIM_PIN_TYPE_PIN1);
        p->pinoperation = htole32(MBIM_PIN_OPERATION_ENTER);
@@ -346,10 +487,32 @@ mbim_config_request(void)
        return mbim_send_command_msg();
 }
 
+static int
+mbim_radio_request(void)
+{
+       if (_argc > 0) {
+               struct mbim_basic_connect_radio_state_s *rs =
+                       (struct mbim_basic_connect_radio_state_s *) mbim_setup_command_msg(basic_connect,
+                               MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_RADIO_STATE,
+                               sizeof(struct mbim_basic_connect_radio_state_r));
+
+               if (!strcmp(_argv[0], "off"))
+                       rs->radiostate = htole32(MBIM_RADIO_SWITCH_STATE_OFF);
+               else
+                       rs->radiostate = htole32(MBIM_RADIO_SWITCH_STATE_ON);
+       } else {
+               mbim_setup_command_msg(basic_connect,
+                       MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_RADIO_STATE,
+                       sizeof(struct mbim_basic_connect_radio_state_r));
+       }
+       return mbim_send_command_msg();
+}
+
 static struct mbim_handler handlers[] = {
        { "caps", 0, mbim_device_caps_request, mbim_device_caps_response },
        { "pinstate", 0, mbim_pin_state_request, mbim_pin_state_response },
        { "unlock", 1, mbim_pin_unlock_request, mbim_pin_state_response },
+       { "home", 0, mbim_home_provider_request, mbim_home_provider_response },
        { "registration", 0, mbim_registration_request, mbim_registration_response },
        { "subscriber", 0, mbim_subscriber_request, mbim_subscriber_response },
        { "attach", 0, mbim_attach_request, mbim_attach_response },
@@ -357,13 +520,17 @@ static struct mbim_handler handlers[] = {
        { "connect", 0, mbim_connect_request, mbim_connect_response },
        { "disconnect", 0, mbim_disconnect_request, mbim_connect_response },
        { "config", 0, mbim_config_request, mbim_config_response },
+       { "radio", 0, mbim_radio_request, mbim_radio_response },
 };
 
 static int
 usage(void)
 {
-       fprintf(stderr, "Usage: mbim <caps|pinstate|unlock|connect|disconnect> [options]\n"
+       fprintf(stderr, "Usage: umbim <caps|pinstate|unlock|home|registration|subscriber|attach|detach|connect|disconnect|config|radio> [options]\n"
                "Options:\n"
+#ifdef LIBQMI_MBIM_PROXY
+               "    -p                 use mbim-proxy\n"
+#endif
                "    -d <device>        the device (/dev/cdc-wdmX)\n"
                "    -t <transaction>   the transaction id\n"
                "    -n                 no close\n\n"
@@ -375,9 +542,13 @@ int
 main(int argc, char **argv)
 {
        char *cmd, *device = NULL;
-       int no_open = 0, ch, i;
+       int no_open = 0, ch;
+       unsigned int i;
+#ifdef LIBQMI_MBIM_PROXY
+       int proxy = 0;
+#endif
 
-       while ((ch = getopt(argc, argv, "nvd:t:")) != -1) {
+       while ((ch = getopt(argc, argv, "pnvd:t:")) != -1) {
                switch (ch) {
                case 'v':
                        verbose = 1;
@@ -392,6 +563,11 @@ main(int argc, char **argv)
                        no_open = 1;
                        transaction_id = atoi(optarg);
                        break;
+#ifdef LIBQMI_MBIM_PROXY
+               case 'p':
+                       proxy = 1;
+                       break;
+#endif
                default:
                        return usage();
                }
@@ -415,6 +591,11 @@ main(int argc, char **argv)
 
        uloop_init();
 
+#ifdef LIBQMI_MBIM_PROXY
+       if (proxy)
+               mbim_proxy_open(device);
+       else
+#endif
        mbim_open(device);
        if (!no_open)
                mbim_send_open_msg();