c43b4f3ed180506ba9a7e12ce18d38a7d366c782
[project/umbim.git] / cli.c
1 /*
2  * umbim
3  * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14
15 #define __STDC_FORMAT_MACROS
16 #include <inttypes.h>
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20
21 #include <alloca.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <ctype.h>
28
29 #include <libubox/utils.h>
30 #include <libubox/uloop.h>
31
32 #include "mbim.h"
33
34 #include "data/mbim-service-basic-connect.h"
35
36 int return_code = -1;
37 int verbose;
38
39 struct mbim_handler *current_handler;
40 static uint8_t uuid_context_type_internet[16] = { 0x7E, 0x5E, 0x2A, 0x7E, 0x4E, 0x6F, 0x72, 0x72, 0x73, 0x6B, 0x65, 0x6E, 0x7E, 0x5E, 0x2A, 0x7E };
41 static int _argc;
42 static char **_argv;
43
44 static int
45 mbim_device_caps_response(void *buffer, int len)
46 {
47         struct mbim_basic_connect_device_caps_r *caps = (struct mbim_basic_connect_device_caps_r *) buffer;
48         char *deviceid, *firmwareinfo, *hardwareinfo;
49
50         if (len < sizeof(struct mbim_basic_connect_device_caps_r)) {
51                 fprintf(stderr, "message not long enough\n");
52                 return -1;
53         }
54
55         deviceid = mbim_get_string(&caps->deviceid, buffer);
56         firmwareinfo = mbim_get_string(&caps->firmwareinfo, buffer);
57         hardwareinfo = mbim_get_string(&caps->hardwareinfo, buffer);
58
59         printf("  devicetype: %04X - %s\n", le32toh(caps->devicetype),
60                 mbim_enum_string(mbim_device_type_values, le32toh(caps->devicetype)));
61         printf("  cellularclass: %04X\n", le32toh(caps->cellularclass));
62         printf("  voiceclass: %04X - %s\n", le32toh(caps->voiceclass),
63                 mbim_enum_string(mbim_voice_class_values, le32toh(caps->voiceclass)));
64         printf("  simclass: %04X\n", le32toh(caps->simclass));
65         printf("  dataclass: %04X\n", le32toh(caps->dataclass));
66         printf("  smscaps: %04X\n", le32toh(caps->smscaps));
67         printf("  controlcaps: %04X\n", le32toh(caps->controlcaps));
68         printf("  maxsessions: %04X\n", le32toh(caps->maxsessions));
69         printf("  deviceid: %s\n", deviceid);
70         printf("  firmwareinfo: %s\n", firmwareinfo);
71         printf("  hardwareinfo: %s\n", hardwareinfo);
72
73         return 0;
74 }
75
76 static int
77 mbim_pin_state_response(void *buffer, int len)
78 {
79         struct mbim_basic_connect_pin_r *pin = (struct mbim_basic_connect_pin_r *) buffer;
80
81         if (len < sizeof(struct mbim_basic_connect_pin_r)) {
82                 fprintf(stderr, "message not long enough\n");
83                 return -1;
84         }
85
86         if (le32toh(pin->pinstate) != MBIM_PIN_STATE_UNLOCKED) {
87                 fprintf(stderr, "required pin: %d - %s\n",
88                         le32toh(pin->pintype), mbim_enum_string(mbim_pin_type_values, le32toh(pin->pintype)));
89                 fprintf(stderr, "remaining attempts: %d\n", le32toh(pin->remainingattempts));
90                 return le32toh(pin->pintype);
91         }
92
93         fprintf(stderr, "Pin Unlocked\n");
94
95         return 0;
96 }
97
98 static int
99 mbim_registration_response(void *buffer, int len)
100 {
101         struct mbim_basic_connect_register_state_r *state = (struct mbim_basic_connect_register_state_r *) buffer;
102         char *provider_id, *provider_name, *roamingtext;
103
104         if (len < sizeof(struct mbim_basic_connect_register_state_r)) {
105                 fprintf(stderr, "message not long enough\n");
106                 return -1;
107         }
108
109         provider_id = mbim_get_string(&state->providerid, buffer);
110         provider_name = mbim_get_string(&state->providername, buffer);
111         roamingtext = mbim_get_string(&state->roamingtext, buffer);
112
113         printf("  nwerror: %04X - %s\n", le32toh(state->nwerror),
114                 mbim_enum_string(mbim_nw_error_values, le32toh(state->nwerror)));
115         printf("  registerstate: %04X - %s\n", le32toh(state->registerstate),
116                 mbim_enum_string(mbim_register_state_values, le32toh(state->registerstate)));
117         printf("  registermode: %04X - %s\n", le32toh(state->registermode),
118                 mbim_enum_string(mbim_register_mode_values, le32toh(state->registermode)));
119         printf("  availabledataclasses: %04X - %s\n", le32toh(state->availabledataclasses),
120                 mbim_enum_string(mbim_data_class_values, le32toh(state->availabledataclasses)));
121         printf("  currentcellularclass: %04X - %s\n", le32toh(state->currentcellularclass),
122                 mbim_enum_string(mbim_cellular_class_values, le32toh(state->currentcellularclass)));
123         printf("  provider_id: %s\n", provider_id);
124         printf("  provider_name: %s\n", provider_name);
125         printf("  roamingtext: %s\n", roamingtext);
126
127         if (le32toh(state->registerstate) == MBIM_REGISTER_STATE_HOME)
128                 return 0;
129
130         return le32toh(state->registerstate);
131 }
132
133 static int
134 mbim_subscriber_response(void *buffer, int len)
135 {
136         struct mbim_basic_connect_subscriber_ready_status_r *state = (struct mbim_basic_connect_subscriber_ready_status_r *) buffer;
137         char *subscriberid, *simiccid;
138         int nr;
139
140         if (len < sizeof(struct mbim_basic_connect_subscriber_ready_status_r)) {
141                 fprintf(stderr, "message not long enough\n");
142                 return -1;
143         }
144
145         subscriberid = mbim_get_string(&state->subscriberid, buffer);
146         simiccid = mbim_get_string(&state->simiccid, buffer);
147
148         printf("  readystate: %04X - %s\n", le32toh(state->readystate),
149                 mbim_enum_string(mbim_subscriber_ready_state_values, le32toh(state->readystate)));
150         printf("  simiccid: %s\n", simiccid);
151         printf("  subscriberid: %s\n", subscriberid);
152         if (le32toh(state->readyinfo) & MBIM_READY_INFO_FLAG_PROTECT_UNIQUE_ID)
153                 printf("  dont display subscriberID: 1\n");
154         for (nr = 0; nr < le32toh(state->telephonenumberscount); nr++) {
155                 struct mbim_string *str = (void *)&state->telephonenumbers + (nr * sizeof(struct mbim_string));
156                 char *number = mbim_get_string(str, buffer);
157                 printf("  number: %s\n", number);
158         }
159
160         if (MBIM_SUBSCRIBER_READY_STATE_INITIALIZED == le32toh(state->readystate))
161                 return 0;
162
163         return le32toh(state->readystate);
164 }
165
166 static int
167 mbim_attach_response(void *buffer, int len)
168 {
169         struct mbim_basic_connect_packet_service_r *ps = (struct mbim_basic_connect_packet_service_r *) buffer;
170
171         if (len < sizeof(struct mbim_basic_connect_packet_service_r)) {
172                 fprintf(stderr, "message not long enough\n");
173                 return -1;
174         }
175
176         printf("  nwerror: %04X - %s\n", le32toh(ps->nwerror),
177                 mbim_enum_string(mbim_nw_error_values, le32toh(ps->nwerror)));
178         printf("  packetservicestate: %04X - %s\n", le32toh(ps->packetservicestate),
179                 mbim_enum_string(mbim_packet_service_state_values, le32toh(ps->packetservicestate)));
180         printf("  uplinkspeed: %"PRIu64"\n", (uint64_t) le64toh(ps->uplinkspeed));
181         printf("  downlinkspeed: %"PRIu64"\n", (uint64_t) le64toh(ps->downlinkspeed));
182
183         if (MBIM_PACKET_SERVICE_STATE_ATTACHED == le32toh(ps->packetservicestate))
184                 return 0;
185
186         return le32toh(ps->packetservicestate);
187 }
188
189 static int
190 mbim_connect_response(void *buffer, int len)
191 {
192         struct mbim_basic_connect_connect_r *c = (struct mbim_basic_connect_connect_r *) buffer;
193
194         if (len < sizeof(struct mbim_basic_connect_connect_r)) {
195                 fprintf(stderr, "message not long enough\n");
196                 return -1;
197         }
198
199         printf("  sessionid: %d\n", le32toh(c->sessionid));
200         printf("  activationstate: %04X - %s\n", le32toh(c->activationstate),
201                 mbim_enum_string(mbim_activation_state_values, le32toh(c->activationstate)));
202         printf("  voicecallstate: %04X - %s\n", le32toh(c->voicecallstate),
203                 mbim_enum_string(mbim_voice_call_state_values, le32toh(c->voicecallstate)));
204         printf("  nwerror: %04X - %s\n", le32toh(c->nwerror),
205                 mbim_enum_string(mbim_nw_error_values, le32toh(c->nwerror)));
206         printf("  iptype: %04X - %s\n", le32toh(c->iptype),
207                 mbim_enum_string(mbim_context_ip_type_values, le32toh(c->iptype)));
208
209         if (MBIM_ACTIVATION_STATE_ACTIVATED == le32toh(c->activationstate))
210                 return 0;
211
212         return le32toh(c->activationstate);
213 }
214
215 static int
216 mbim_config_response(void *buffer, int len)
217 {
218         struct mbim_basic_connect_ip_configuration_r *ip = (struct mbim_basic_connect_ip_configuration_r *) buffer;
219         char ipv4[16];
220         int i;
221         uint32_t offset;
222
223         if (len < sizeof(struct mbim_basic_connect_ip_configuration_r)) {
224                 fprintf(stderr, "message not long enough\n");
225                 return -1;
226         }
227
228         if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS)
229                 for (i = 0; i < le32toh(ip->ipv4addresscount); i++) {
230                         offset = le32toh(ip->ipv4address) + (i * 4);
231                         mbim_get_ipv4(buffer, ipv4, 4 + offset);
232                         printf("  ipv4address: %s/%d\n", ipv4, mbim_get_int(buffer, offset));
233                 }
234         if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) {
235                 mbim_get_ipv4(buffer, ipv4, ip->ipv4gateway);
236                 printf("  ipv4gateway: %s\n", ipv4);
237         }
238         if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
239                 printf("  ipv4mtu: %d\n", le32toh(ip->ipv4mtu));
240         if (le32toh(ip->ipv4configurationavailable) & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS)
241                 for (i = 0; i < le32toh(ip->ipv4dnsservercount); i++) {
242                         mbim_get_ipv4(buffer, ipv4, ip->ipv4dnsserver + (i * 4));
243                         printf("  ipv4dnsserver: %s\n", ipv4);
244                 }
245
246         printf("  ipv6configurationavailable: %04X\n", le32toh(ip->ipv6configurationavailable));
247
248         return 0;
249 }
250
251 static int
252 mbim_device_caps_request(void)
253 {
254         mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_DEVICE_CAPS, 0);
255
256         return mbim_send_command_msg();
257 }
258
259 static int
260 mbim_pin_state_request(void)
261 {
262         mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_PIN, 0);
263
264         return mbim_send_command_msg();
265 }
266
267 static int
268 mbim_registration_request(void)
269 {
270         mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_REGISTER_STATE, 0);
271
272         return mbim_send_command_msg();
273 }
274
275 static int
276 mbim_subscriber_request(void)
277 {
278         mbim_setup_command_msg(basic_connect, MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_SUBSCRIBER_READY_STATUS, 0);
279
280         return mbim_send_command_msg();
281 }
282
283 static int
284 _mbim_attach_request(int action)
285 {
286         struct mbim_basic_connect_packet_service_s *ps =
287                 (struct mbim_basic_connect_packet_service_s *) mbim_setup_command_msg(basic_connect,
288                         MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_PACKET_SERVICE,
289                         sizeof(struct mbim_basic_connect_packet_service_s));
290
291         ps->packetserviceaction = htole32(action);
292
293         return mbim_send_command_msg();
294 }
295
296 static int
297 mbim_attach_request(void)
298 {
299         return _mbim_attach_request(MBIM_PACKET_SERVICE_ACTION_ATTACH);
300 }
301
302 static int
303 mbim_detach_request(void)
304 {
305         return _mbim_attach_request(MBIM_PACKET_SERVICE_ACTION_DETACH);
306 }
307
308 static int
309 mbim_connect_request(void)
310 {
311         struct mbim_basic_connect_connect_s *c =
312                 (struct mbim_basic_connect_connect_s *) mbim_setup_command_msg(basic_connect,
313                         MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_CONNECT,
314                         sizeof(struct mbim_basic_connect_connect_s));
315
316         c->activationcommand = htole32(MBIM_ACTIVATION_COMMAND_ACTIVATE);
317         c->iptype = htole32(MBIM_CONTEXT_IP_TYPE_DEFAULT);
318         memcpy(c->contexttype, uuid_context_type_internet, 16);
319         if (_argc > 0)
320                 mbim_encode_string(&c->accessstring, *_argv);
321         if (_argc > 3) {
322                 if (!strcmp(_argv[1], "pap"))
323                         c->authprotocol = htole32(MBIM_AUTH_PROTOCOL_PAP);
324                 else if (!strcmp(_argv[1], "chap"))
325                         c->authprotocol = htole32(MBIM_AUTH_PROTOCOL_CHAP);
326                 else if (!strcmp(_argv[1], "mschapv2"))
327                         c->authprotocol = htole32(MBIM_AUTH_PROTOCOL_MSCHAPV2);
328
329                 if (c->authprotocol) {
330                         mbim_encode_string(&c->username, _argv[2]);
331                         mbim_encode_string(&c->password, _argv[3]);
332                 }
333         }
334         return mbim_send_command_msg();
335 }
336
337 static int
338 mbim_disconnect_request(void)
339 {
340         struct mbim_basic_connect_connect_s *c =
341                 (struct mbim_basic_connect_connect_s *) mbim_setup_command_msg(basic_connect,
342                         MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_CONNECT,
343                         sizeof(struct mbim_basic_connect_connect_s));
344
345         c->activationcommand = htole32(MBIM_ACTIVATION_COMMAND_DEACTIVATE);
346         memcpy(c->contexttype, uuid_context_type_internet, 16);
347
348         no_close = 0;
349
350         return mbim_send_command_msg();
351 }
352
353 static char*
354 mbim_pin_sanitize(char *pin)
355 {
356         char *p;
357
358         while (*pin && !isdigit(*pin))
359                 pin++;
360         p = pin;
361         if (!*p)
362                 return NULL;
363         while (*pin && isdigit(*pin))
364                 pin++;
365         if (*pin)
366                 *pin = '\0';
367
368         return p;
369 }
370
371 static int
372 mbim_pin_unlock_request(void)
373 {
374         struct mbim_basic_connect_pin_s *p =
375                 (struct mbim_basic_connect_pin_s *) mbim_setup_command_msg(basic_connect,
376                         MBIM_MESSAGE_COMMAND_TYPE_SET, MBIM_CMD_BASIC_CONNECT_PIN,
377                         sizeof(struct mbim_basic_connect_pin_s));
378         char *pin = mbim_pin_sanitize(_argv[0]);
379
380         if (!pin || !strlen(pin)) {
381                 fprintf(stderr, "failed to sanitize the pincode\n");
382                 return -1;
383         }
384
385         p->pintype = htole32(MBIM_PIN_TYPE_PIN1);
386         p->pinoperation = htole32(MBIM_PIN_OPERATION_ENTER);
387         mbim_encode_string(&p->pin, _argv[0]);
388
389         return mbim_send_command_msg();
390 }
391
392 static int
393 mbim_config_request(void)
394 {
395         mbim_setup_command_msg(basic_connect,
396                 MBIM_MESSAGE_COMMAND_TYPE_QUERY, MBIM_CMD_BASIC_CONNECT_IP_CONFIGURATION,
397                 sizeof(struct mbim_basic_connect_ip_configuration_q));
398
399         return mbim_send_command_msg();
400 }
401
402 static struct mbim_handler handlers[] = {
403         { "caps", 0, mbim_device_caps_request, mbim_device_caps_response },
404         { "pinstate", 0, mbim_pin_state_request, mbim_pin_state_response },
405         { "unlock", 1, mbim_pin_unlock_request, mbim_pin_state_response },
406         { "registration", 0, mbim_registration_request, mbim_registration_response },
407         { "subscriber", 0, mbim_subscriber_request, mbim_subscriber_response },
408         { "attach", 0, mbim_attach_request, mbim_attach_response },
409         { "detach", 0, mbim_detach_request, mbim_attach_response },
410         { "connect", 0, mbim_connect_request, mbim_connect_response },
411         { "disconnect", 0, mbim_disconnect_request, mbim_connect_response },
412         { "config", 0, mbim_config_request, mbim_config_response },
413 };
414
415 static int
416 usage(void)
417 {
418         fprintf(stderr, "Usage: mbim <caps|pinstate|unlock|connect|disconnect> [options]\n"
419                 "Options:\n"
420                 "    -d <device>        the device (/dev/cdc-wdmX)\n"
421                 "    -t <transaction>   the transaction id\n"
422                 "    -n                 no close\n\n"
423                 "    -v                 verbose\n\n");
424         return 1;
425 }
426
427 int
428 main(int argc, char **argv)
429 {
430         char *cmd, *device = NULL;
431         int no_open = 0, ch, i;
432
433         while ((ch = getopt(argc, argv, "nvd:t:")) != -1) {
434                 switch (ch) {
435                 case 'v':
436                         verbose = 1;
437                         break;
438                 case 'n':
439                         no_close = 1;
440                         break;
441                 case 'd':
442                         device = optarg;
443                         break;
444                 case 't':
445                         no_open = 1;
446                         transaction_id = atoi(optarg);
447                         break;
448                 default:
449                         return usage();
450                 }
451         }
452
453         if (!device || optind == argc)
454                 return usage();
455
456         cmd = argv[optind];
457         optind++;
458
459         _argc = argc - optind;
460         _argv = &argv[optind];
461
462         for (i = 0; i < ARRAY_SIZE(handlers); i++)
463                 if (!strcmp(cmd, handlers[i].name))
464                         current_handler = &handlers[i];
465
466         if (!current_handler || (optind + current_handler->argc > argc))
467                 return usage();
468
469         uloop_init();
470
471         mbim_open(device);
472         if (!no_open)
473                 mbim_send_open_msg();
474         else if (current_handler->request() < 0)
475                 return -1;
476
477         uloop_run();
478         uloop_done();
479
480         return return_code;
481 }