1*2478cc98Skhorben /* $NetBSD: umbctl.c,v 1.4 2020/05/13 21:44:30 khorben Exp $ */
2d7f036beSkhorben /*
3d7f036beSkhorben * Copyright (c) 2018 Pierre Pronchery <khorben@defora.org>
4d7f036beSkhorben *
5d7f036beSkhorben * All rights reserved.
6d7f036beSkhorben *
7d7f036beSkhorben * Redistribution and use in source and binary forms, with or without
8d7f036beSkhorben * modification, are permitted provided that the following conditions
9d7f036beSkhorben * are met:
10d7f036beSkhorben * 1. Redistributions of source code must retain the above copyright
11d7f036beSkhorben * notice, this list of conditions and the following disclaimer.
12d7f036beSkhorben * 2. Redistributions in binary form must reproduce the above copyright
13d7f036beSkhorben * notice, this list of conditions and the following disclaimer in the
14d7f036beSkhorben * documentation and/or other materials provided with the distribution.
15d7f036beSkhorben *
16d7f036beSkhorben * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
17d7f036beSkhorben * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18d7f036beSkhorben * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19d7f036beSkhorben * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
20d7f036beSkhorben * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21d7f036beSkhorben * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22d7f036beSkhorben * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23d7f036beSkhorben * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24d7f036beSkhorben * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25d7f036beSkhorben * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26d7f036beSkhorben */
27d7f036beSkhorben
28d7f036beSkhorben
29d7f036beSkhorben
30d7f036beSkhorben #include <sys/endian.h>
31d7f036beSkhorben #include <sys/ioctl.h>
32d7f036beSkhorben #include <sys/socket.h>
33d7f036beSkhorben
34d7f036beSkhorben #include <net/if.h>
35d7f036beSkhorben
36d7f036beSkhorben #include <ctype.h>
37d7f036beSkhorben #include <errno.h>
38d7f036beSkhorben #include <stdarg.h>
39d7f036beSkhorben #include <stdio.h>
40d7f036beSkhorben #include <string.h>
41d7f036beSkhorben #include <unistd.h>
42d7f036beSkhorben
43d7f036beSkhorben #include <dev/usb/mbim.h>
44d7f036beSkhorben #include <dev/usb/if_umbreg.h>
45d7f036beSkhorben
46d7f036beSkhorben
47d7f036beSkhorben /* constants */
48d7f036beSkhorben static const struct umb_valdescr _umb_regstate[] =
49d7f036beSkhorben MBIM_REGSTATE_DESCRIPTIONS;
50d7f036beSkhorben
51d7f036beSkhorben static const struct umb_valdescr _umb_dataclass[] =
52d7f036beSkhorben MBIM_DATACLASS_DESCRIPTIONS;
53d7f036beSkhorben
54d7f036beSkhorben static const struct umb_valdescr _umb_state[] =
55d7f036beSkhorben UMB_INTERNAL_STATE_DESCRIPTIONS;
56d7f036beSkhorben
57d7f036beSkhorben static const struct umb_valdescr _umb_regmode[] =
58d7f036beSkhorben {
59d7f036beSkhorben { MBIM_REGMODE_UNKNOWN, "unknown" },
60d7f036beSkhorben { MBIM_REGMODE_AUTOMATIC, "automatic" },
61d7f036beSkhorben { MBIM_REGMODE_MANUAL, "manual" },
62d7f036beSkhorben { 0, NULL }
63d7f036beSkhorben };
64d7f036beSkhorben
65d7f036beSkhorben static const struct umb_valdescr _umb_ber[] =
66d7f036beSkhorben {
67d7f036beSkhorben { UMB_BER_EXCELLENT, "excellent" },
68d7f036beSkhorben { UMB_BER_VERYGOOD, "very good" },
69d7f036beSkhorben { UMB_BER_GOOD, "good" },
70d7f036beSkhorben { UMB_BER_OK, "ok" },
71d7f036beSkhorben { UMB_BER_MEDIUM, "medium" },
72d7f036beSkhorben { UMB_BER_BAD, "bad" },
73d7f036beSkhorben { UMB_BER_VERYBAD, "very bad" },
74d7f036beSkhorben { UMB_BER_EXTREMELYBAD, "extremely bad" },
75d7f036beSkhorben { 0, NULL }
76d7f036beSkhorben };
77d7f036beSkhorben
78d7f036beSkhorben
79d7f036beSkhorben /* prototypes */
80d7f036beSkhorben static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen);
81d7f036beSkhorben static int _error(int ret, char const * format, ...);
82d7f036beSkhorben static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]);
839e6960f9Skhorben static int _umbctl_file(char const * ifname, char const * filename,
849e6960f9Skhorben int verbose);
85d7f036beSkhorben static void _umbctl_info(char const * ifname, struct umb_info * umbi);
86d7f036beSkhorben static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request,
87d7f036beSkhorben struct ifreq * ifr);
88d7f036beSkhorben static int _umbctl_set(char const * ifname, struct umb_parameter * umbp,
89d7f036beSkhorben int argc, char * argv[]);
90d7f036beSkhorben static int _umbctl_socket(void);
91d7f036beSkhorben static int _usage(void);
92d7f036beSkhorben static void _utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen);
93d7f036beSkhorben
94d7f036beSkhorben
95d7f036beSkhorben /* functions */
96d7f036beSkhorben /* char_to_utf16 */
97d7f036beSkhorben /* this function is from OpenBSD's ifconfig(8) */
_char_to_utf16(const char * in,uint16_t * out,size_t outlen)98d7f036beSkhorben static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen)
99d7f036beSkhorben {
100d7f036beSkhorben int n = 0;
101d7f036beSkhorben uint16_t c;
102d7f036beSkhorben
103d7f036beSkhorben for (;;) {
104d7f036beSkhorben c = *in++;
105d7f036beSkhorben
106d7f036beSkhorben if (c == '\0') {
107d7f036beSkhorben /*
108d7f036beSkhorben * NUL termination is not required, but zero out the
109d7f036beSkhorben * residual buffer
110d7f036beSkhorben */
111d7f036beSkhorben memset(out, 0, outlen);
112d7f036beSkhorben return n;
113d7f036beSkhorben }
114d7f036beSkhorben if (outlen < sizeof(*out))
115d7f036beSkhorben return -1;
116d7f036beSkhorben
117d7f036beSkhorben *out++ = htole16(c);
118d7f036beSkhorben n += sizeof(*out);
119d7f036beSkhorben outlen -= sizeof(*out);
120d7f036beSkhorben }
121d7f036beSkhorben }
122d7f036beSkhorben
123d7f036beSkhorben
124d7f036beSkhorben /* error */
_error(int ret,char const * format,...)12576fcbe2eSroy __printflike(2, 3) static int _error(int ret, char const * format, ...)
126d7f036beSkhorben {
127d7f036beSkhorben va_list ap;
128d7f036beSkhorben
129d7f036beSkhorben fputs("umbctl: ", stderr);
130d7f036beSkhorben va_start(ap, format);
131d7f036beSkhorben vfprintf(stderr, format, ap);
132d7f036beSkhorben va_end(ap);
133d7f036beSkhorben fputs("\n", stderr);
134d7f036beSkhorben return ret;
135d7f036beSkhorben }
136d7f036beSkhorben
137d7f036beSkhorben
138d7f036beSkhorben /* umbctl */
_umbctl(char const * ifname,int verbose,int argc,char * argv[])139d7f036beSkhorben static int _umbctl(char const * ifname, int verbose, int argc, char * argv[])
140d7f036beSkhorben {
141d7f036beSkhorben int fd;
142d7f036beSkhorben struct ifreq ifr;
143d7f036beSkhorben struct umb_info umbi;
144d7f036beSkhorben struct umb_parameter umbp;
145d7f036beSkhorben
146d7f036beSkhorben if((fd = _umbctl_socket()) < 0)
147d7f036beSkhorben return 2;
148d7f036beSkhorben memset(&ifr, 0, sizeof(ifr));
149d7f036beSkhorben strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
150d7f036beSkhorben if(argc != 0)
151d7f036beSkhorben {
152d7f036beSkhorben memset(&umbp, 0, sizeof(umbp));
153d7f036beSkhorben ifr.ifr_data = &umbp;
154d7f036beSkhorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0
155d7f036beSkhorben || _umbctl_set(ifname, &umbp, argc, argv) != 0
156d7f036beSkhorben || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM,
157d7f036beSkhorben &ifr) != 0)
158d7f036beSkhorben {
159d7f036beSkhorben close(fd);
160d7f036beSkhorben return 2;
161d7f036beSkhorben }
162d7f036beSkhorben }
163d7f036beSkhorben if(argc == 0 || verbose > 0)
164d7f036beSkhorben {
165d7f036beSkhorben ifr.ifr_data = &umbi;
166d7f036beSkhorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0)
167d7f036beSkhorben {
168d7f036beSkhorben close(fd);
169d7f036beSkhorben return 3;
170d7f036beSkhorben }
171d7f036beSkhorben _umbctl_info(ifname, &umbi);
172d7f036beSkhorben }
173d7f036beSkhorben if(close(fd) != 0)
174d7f036beSkhorben return _error(2, "%s: %s", ifname, strerror(errno));
175d7f036beSkhorben return 0;
176d7f036beSkhorben }
177d7f036beSkhorben
178d7f036beSkhorben
179d7f036beSkhorben /* umbctl_file */
_umbctl_file(char const * ifname,char const * filename,int verbose)1809e6960f9Skhorben static int _umbctl_file(char const * ifname, char const * filename, int verbose)
181d7f036beSkhorben {
182*2478cc98Skhorben int ret = 0;
183d7f036beSkhorben int fd;
184d7f036beSkhorben struct ifreq ifr;
185d7f036beSkhorben struct umb_info umbi;
186d7f036beSkhorben struct umb_parameter umbp;
187d7f036beSkhorben FILE * fp;
188d7f036beSkhorben char buf[512];
189*2478cc98Skhorben size_t len;
190*2478cc98Skhorben int i;
191d7f036beSkhorben int eof;
192d7f036beSkhorben char * tokens[3] = { buf, NULL, NULL };
193d7f036beSkhorben char * p;
194d7f036beSkhorben
195d7f036beSkhorben if((fp = fopen(filename, "r")) == NULL)
196d7f036beSkhorben return _error(2, "%s: %s", filename, strerror(errno));
197d7f036beSkhorben memset(&umbp, 0, sizeof(umbp));
198d7f036beSkhorben while(fgets(buf, sizeof(buf), fp) != NULL)
199d7f036beSkhorben {
200d7f036beSkhorben if(buf[0] == '#')
201d7f036beSkhorben continue;
202d7f036beSkhorben buf[sizeof(buf) - 1] = '\0';
203*2478cc98Skhorben if((len = strlen(buf)) > 0)
204*2478cc98Skhorben {
205*2478cc98Skhorben if(buf[len - 1] != '\n')
206*2478cc98Skhorben {
207*2478cc98Skhorben ret = _error(2, "%s: %s", filename,
208*2478cc98Skhorben "Line too long");
209*2478cc98Skhorben while((i = fgetc(fp)) != EOF && i != '\n');
210*2478cc98Skhorben continue;
211*2478cc98Skhorben }
212*2478cc98Skhorben else
213*2478cc98Skhorben buf[len - 1] = '\0';
214*2478cc98Skhorben }
215*2478cc98Skhorben if((p = strchr(buf, '=')) != NULL)
216d7f036beSkhorben {
217d7f036beSkhorben tokens[1] = p + 1;
218d7f036beSkhorben *p = '\0';
219d7f036beSkhorben } else
220d7f036beSkhorben tokens[1] = NULL;
221*2478cc98Skhorben ret |= _umbctl_set(ifname, &umbp, (p != NULL) ? 2 : 1, tokens)
222*2478cc98Skhorben ? 2 : 0;
223d7f036beSkhorben }
224d7f036beSkhorben eof = feof(fp);
225d7f036beSkhorben if(fclose(fp) != 0 || !eof)
226d7f036beSkhorben return _error(2, "%s: %s", filename, strerror(errno));
227*2478cc98Skhorben if(ret != 0)
228*2478cc98Skhorben return ret;
229d7f036beSkhorben if((fd = _umbctl_socket()) < 0)
230d7f036beSkhorben return 2;
231d7f036beSkhorben memset(&ifr, 0, sizeof(ifr));
232d7f036beSkhorben strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
233d7f036beSkhorben ifr.ifr_data = &umbp;
234d7f036beSkhorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0
235d7f036beSkhorben || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, &ifr) != 0)
236d7f036beSkhorben {
237d7f036beSkhorben close(fd);
238d7f036beSkhorben return 2;
239d7f036beSkhorben }
240d7f036beSkhorben if(verbose > 0)
241d7f036beSkhorben {
242d7f036beSkhorben ifr.ifr_data = &umbi;
243d7f036beSkhorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0)
244d7f036beSkhorben {
245d7f036beSkhorben close(fd);
246d7f036beSkhorben return 3;
247d7f036beSkhorben }
248d7f036beSkhorben _umbctl_info(ifname, &umbi);
249d7f036beSkhorben }
250d7f036beSkhorben if(close(fd) != 0)
251d7f036beSkhorben return _error(2, "%s: %s", ifname, strerror(errno));
252d7f036beSkhorben return 0;
253d7f036beSkhorben }
254d7f036beSkhorben
255d7f036beSkhorben
256d7f036beSkhorben /* umbctl_info */
_umbctl_info(char const * ifname,struct umb_info * umbi)257d7f036beSkhorben static void _umbctl_info(char const * ifname, struct umb_info * umbi)
258d7f036beSkhorben {
259d7f036beSkhorben char provider[UMB_PROVIDERNAME_MAXLEN + 1];
260d7f036beSkhorben char pn[UMB_PHONENR_MAXLEN + 1];
261d7f036beSkhorben char roaming[UMB_ROAMINGTEXT_MAXLEN + 1];
262d7f036beSkhorben char apn[UMB_APN_MAXLEN + 1];
263d7f036beSkhorben char fwinfo[UMB_FWINFO_MAXLEN + 1];
264d7f036beSkhorben char hwinfo[UMB_HWINFO_MAXLEN + 1];
265d7f036beSkhorben
266d7f036beSkhorben _utf16_to_char(umbi->provider, UMB_PROVIDERNAME_MAXLEN,
267d7f036beSkhorben provider, sizeof(provider));
268d7f036beSkhorben _utf16_to_char(umbi->pn, UMB_PHONENR_MAXLEN, pn, sizeof(pn));
269d7f036beSkhorben _utf16_to_char(umbi->roamingtxt, UMB_ROAMINGTEXT_MAXLEN,
270d7f036beSkhorben roaming, sizeof(roaming));
271d7f036beSkhorben _utf16_to_char(umbi->apn, UMB_APN_MAXLEN, apn, sizeof(apn));
272d7f036beSkhorben _utf16_to_char(umbi->fwinfo, UMB_FWINFO_MAXLEN, fwinfo, sizeof(fwinfo));
273d7f036beSkhorben _utf16_to_char(umbi->hwinfo, UMB_HWINFO_MAXLEN, hwinfo, sizeof(hwinfo));
274d7f036beSkhorben printf("%s: state %s, mode %s, registration %s\n"
275d7f036beSkhorben "\tprovider \"%s\", dataclass %s, signal %s\n"
276d7f036beSkhorben "\tphone number \"%s\", roaming \"%s\" (%s)\n"
277d7f036beSkhorben "\tAPN \"%s\", TX %" PRIu64 ", RX %" PRIu64 "\n"
278d7f036beSkhorben "\tfirmware \"%s\", hardware \"%s\"\n",
279d7f036beSkhorben ifname, umb_val2descr(_umb_state, umbi->state),
280d7f036beSkhorben umb_val2descr(_umb_regmode, umbi->regmode),
281d7f036beSkhorben umb_val2descr(_umb_regstate, umbi->regstate), provider,
282d7f036beSkhorben umb_val2descr(_umb_dataclass, umbi->cellclass),
283d7f036beSkhorben umb_val2descr(_umb_ber, umbi->ber), pn, roaming,
284d7f036beSkhorben umbi->enable_roaming ? "allowed" : "denied",
285d7f036beSkhorben apn, umbi->uplink_speed, umbi->downlink_speed,
286d7f036beSkhorben fwinfo, hwinfo);
287d7f036beSkhorben }
288d7f036beSkhorben
289d7f036beSkhorben
290d7f036beSkhorben /* umbctl_ioctl */
_umbctl_ioctl(char const * ifname,int fd,unsigned long request,struct ifreq * ifr)291d7f036beSkhorben static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request,
292d7f036beSkhorben struct ifreq * ifr)
293d7f036beSkhorben {
294d7f036beSkhorben if(ioctl(fd, request, ifr) != 0)
295d7f036beSkhorben return _error(-1, "%s: %s", ifname, strerror(errno));
296d7f036beSkhorben return 0;
297d7f036beSkhorben }
298d7f036beSkhorben
299d7f036beSkhorben
300d7f036beSkhorben /* umbctl_set */
301d7f036beSkhorben /* callbacks */
302d7f036beSkhorben static int _set_apn(char const *, struct umb_parameter *, char const *);
303d7f036beSkhorben static int _set_username(char const *, struct umb_parameter *, char const *);
304d7f036beSkhorben static int _set_password(char const *, struct umb_parameter *, char const *);
305d7f036beSkhorben static int _set_pin(char const *, struct umb_parameter *, char const *);
306d7f036beSkhorben static int _set_puk(char const *, struct umb_parameter *, char const *);
307d7f036beSkhorben static int _set_roaming_allow(char const *, struct umb_parameter *,
308d7f036beSkhorben char const *);
309d7f036beSkhorben static int _set_roaming_deny(char const *, struct umb_parameter *,
310d7f036beSkhorben char const *);
311d7f036beSkhorben
_umbctl_set(char const * ifname,struct umb_parameter * umbp,int argc,char * argv[])312d7f036beSkhorben static int _umbctl_set(char const * ifname, struct umb_parameter * umbp,
313d7f036beSkhorben int argc, char * argv[])
314d7f036beSkhorben {
315d7f036beSkhorben struct
316d7f036beSkhorben {
317d7f036beSkhorben char const * name;
318d7f036beSkhorben int (*callback)(char const *,
319d7f036beSkhorben struct umb_parameter *, char const *);
320d7f036beSkhorben int parameter;
321d7f036beSkhorben } callbacks[] =
322d7f036beSkhorben {
323d7f036beSkhorben { "apn", _set_apn, 1 },
324d7f036beSkhorben { "username", _set_username, 1 },
325d7f036beSkhorben { "password", _set_password, 1 },
326d7f036beSkhorben { "pin", _set_pin, 1 },
327d7f036beSkhorben { "puk", _set_puk, 1 },
328d7f036beSkhorben { "roaming", _set_roaming_allow, 0 },
329d7f036beSkhorben { "-roaming", _set_roaming_deny, 0 },
330d7f036beSkhorben };
331d7f036beSkhorben int i;
332d7f036beSkhorben size_t j;
333d7f036beSkhorben
334d7f036beSkhorben for(i = 0; i < argc; i++)
335d7f036beSkhorben {
336d7f036beSkhorben for(j = 0; j < sizeof(callbacks) / sizeof(*callbacks); j++)
337d7f036beSkhorben if(strcmp(argv[i], callbacks[j].name) == 0)
338d7f036beSkhorben {
339d7f036beSkhorben if(callbacks[j].parameter && i + 1 == argc)
340d7f036beSkhorben return _error(-1, "%s: Incomplete"
341d7f036beSkhorben " parameter", argv[i]);
342d7f036beSkhorben if(callbacks[j].callback(ifname, umbp,
343d7f036beSkhorben callbacks[j].parameter
344d7f036beSkhorben ? argv[i + 1] : NULL))
345d7f036beSkhorben return -1;
346d7f036beSkhorben if(callbacks[j].parameter)
347d7f036beSkhorben i++;
348d7f036beSkhorben break;
349d7f036beSkhorben }
350d7f036beSkhorben if(j == sizeof(callbacks) / sizeof(*callbacks))
351d7f036beSkhorben return _error(-1, "%s: Unknown parameter", argv[i]);
352d7f036beSkhorben }
353d7f036beSkhorben return 0;
354d7f036beSkhorben }
355d7f036beSkhorben
_set_apn(char const * ifname,struct umb_parameter * umbp,char const * apn)356d7f036beSkhorben static int _set_apn(char const * ifname, struct umb_parameter * umbp,
357d7f036beSkhorben char const * apn)
358d7f036beSkhorben {
359d7f036beSkhorben umbp->apnlen = _char_to_utf16(apn, umbp->apn, sizeof(umbp->apn));
360d7f036beSkhorben if(umbp->apnlen < 0 || (size_t)umbp->apnlen > sizeof(umbp->apn))
361d7f036beSkhorben return _error(-1, "%s: %s", ifname, "APN too long");
362d7f036beSkhorben return 0;
363d7f036beSkhorben }
364d7f036beSkhorben
_set_username(char const * ifname,struct umb_parameter * umbp,char const * username)365d7f036beSkhorben static int _set_username(char const * ifname, struct umb_parameter * umbp,
366d7f036beSkhorben char const * username)
367d7f036beSkhorben {
368d7f036beSkhorben umbp->usernamelen = _char_to_utf16(username, umbp->username,
369d7f036beSkhorben sizeof(umbp->username));
370d7f036beSkhorben if(umbp->usernamelen < 0
371d7f036beSkhorben || (size_t)umbp->usernamelen > sizeof(umbp->username))
372d7f036beSkhorben return _error(-1, "%s: %s", ifname, "Username too long");
373d7f036beSkhorben return 0;
374d7f036beSkhorben }
375d7f036beSkhorben
_set_password(char const * ifname,struct umb_parameter * umbp,char const * password)376d7f036beSkhorben static int _set_password(char const * ifname, struct umb_parameter * umbp,
377d7f036beSkhorben char const * password)
378d7f036beSkhorben {
379d7f036beSkhorben umbp->passwordlen = _char_to_utf16(password, umbp->password,
380d7f036beSkhorben sizeof(umbp->password));
381d7f036beSkhorben if(umbp->passwordlen < 0
382d7f036beSkhorben || (size_t)umbp->passwordlen > sizeof(umbp->password))
383d7f036beSkhorben return _error(-1, "%s: %s", ifname, "Password too long");
384d7f036beSkhorben return 0;
385d7f036beSkhorben }
386d7f036beSkhorben
_set_pin(char const * ifname,struct umb_parameter * umbp,char const * pin)387d7f036beSkhorben static int _set_pin(char const * ifname, struct umb_parameter * umbp,
388d7f036beSkhorben char const * pin)
389d7f036beSkhorben {
390d7f036beSkhorben umbp->is_puk = 0;
391d7f036beSkhorben umbp->op = MBIM_PIN_OP_ENTER;
392d7f036beSkhorben umbp->pinlen = _char_to_utf16(pin, umbp->pin, sizeof(umbp->pin));
393d7f036beSkhorben if(umbp->pinlen < 0 || (size_t)umbp->pinlen
394d7f036beSkhorben > sizeof(umbp->pin))
395d7f036beSkhorben return _error(-1, "%s: %s", ifname, "PIN code too long");
396d7f036beSkhorben return 0;
397d7f036beSkhorben }
398d7f036beSkhorben
_set_puk(char const * ifname,struct umb_parameter * umbp,char const * puk)399d7f036beSkhorben static int _set_puk(char const * ifname, struct umb_parameter * umbp,
400d7f036beSkhorben char const * puk)
401d7f036beSkhorben {
402d7f036beSkhorben umbp->is_puk = 1;
403d7f036beSkhorben umbp->op = MBIM_PIN_OP_ENTER;
404d7f036beSkhorben umbp->pinlen = _char_to_utf16(puk, umbp->pin, sizeof(umbp->pin));
405d7f036beSkhorben if(umbp->pinlen < 0 || (size_t)umbp->pinlen > sizeof(umbp->pin))
406d7f036beSkhorben return _error(-1, "%s: %s", ifname, "PUK code too long");
407d7f036beSkhorben return 0;
408d7f036beSkhorben }
409d7f036beSkhorben
_set_roaming_allow(char const * ifname,struct umb_parameter * umbp,char const * unused)410d7f036beSkhorben static int _set_roaming_allow(char const * ifname, struct umb_parameter * umbp,
411d7f036beSkhorben char const * unused)
412d7f036beSkhorben {
413d7f036beSkhorben (void) ifname;
414d7f036beSkhorben (void) unused;
415d7f036beSkhorben
416d7f036beSkhorben umbp->roaming = 1;
417d7f036beSkhorben return 0;
418d7f036beSkhorben }
419d7f036beSkhorben
_set_roaming_deny(char const * ifname,struct umb_parameter * umbp,char const * unused)420d7f036beSkhorben static int _set_roaming_deny(char const * ifname, struct umb_parameter * umbp,
421d7f036beSkhorben char const * unused)
422d7f036beSkhorben {
423d7f036beSkhorben (void) ifname;
424d7f036beSkhorben (void) unused;
425d7f036beSkhorben
426d7f036beSkhorben umbp->roaming = 0;
427d7f036beSkhorben return 0;
428d7f036beSkhorben }
429d7f036beSkhorben
430d7f036beSkhorben
431d7f036beSkhorben /* umbctl_socket */
_umbctl_socket(void)432d7f036beSkhorben static int _umbctl_socket(void)
433d7f036beSkhorben {
434d7f036beSkhorben int fd;
435d7f036beSkhorben
436d7f036beSkhorben if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
437d7f036beSkhorben return _error(-1, "socket: %s", strerror(errno));
438d7f036beSkhorben return fd;
439d7f036beSkhorben }
440d7f036beSkhorben
441d7f036beSkhorben
442d7f036beSkhorben /* usage */
_usage(void)443d7f036beSkhorben static int _usage(void)
444d7f036beSkhorben {
4459e6960f9Skhorben fputs("Usage: umbctl [-v] ifname [parameter [value]] [...]\n"
446d7f036beSkhorben " umbctl -f config-file ifname [...]\n",
447d7f036beSkhorben stderr);
448d7f036beSkhorben return 1;
449d7f036beSkhorben }
450d7f036beSkhorben
451d7f036beSkhorben
452d7f036beSkhorben /* utf16_to_char */
_utf16_to_char(uint16_t * in,int inlen,char * out,size_t outlen)453d7f036beSkhorben static void _utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen)
454d7f036beSkhorben {
455d7f036beSkhorben uint16_t c;
456d7f036beSkhorben
457d7f036beSkhorben while (outlen > 0) {
458d7f036beSkhorben c = inlen > 0 ? htole16(*in) : 0;
459d7f036beSkhorben if (c == 0 || --outlen == 0) {
460d7f036beSkhorben /* always NUL terminate result */
461d7f036beSkhorben *out = '\0';
462d7f036beSkhorben break;
463d7f036beSkhorben }
464d7f036beSkhorben *out++ = isascii(c) ? (char)c : '?';
465d7f036beSkhorben in++;
466d7f036beSkhorben inlen--;
467d7f036beSkhorben }
468d7f036beSkhorben }
469d7f036beSkhorben
470d7f036beSkhorben
471d7f036beSkhorben /* main */
main(int argc,char * argv[])472d7f036beSkhorben int main(int argc, char * argv[])
473d7f036beSkhorben {
474d7f036beSkhorben int o;
475d7f036beSkhorben char const * filename = NULL;
476d7f036beSkhorben int verbose = 0;
477d7f036beSkhorben
478d7f036beSkhorben while((o = getopt(argc, argv, "f:v")) != -1)
479d7f036beSkhorben switch(o)
480d7f036beSkhorben {
481d7f036beSkhorben case 'f':
482d7f036beSkhorben filename = optarg;
483d7f036beSkhorben break;
484d7f036beSkhorben case 'v':
485d7f036beSkhorben verbose++;
486d7f036beSkhorben break;
487d7f036beSkhorben default:
488d7f036beSkhorben return _usage();
489d7f036beSkhorben }
490d7f036beSkhorben if(optind == argc)
491d7f036beSkhorben return _usage();
492d7f036beSkhorben if(filename != NULL)
4939e6960f9Skhorben {
4949e6960f9Skhorben if(optind + 1 != argc)
4959e6960f9Skhorben return _usage();
4969e6960f9Skhorben return _umbctl_file(argv[optind], filename, verbose);
4979e6960f9Skhorben }
498d7f036beSkhorben return _umbctl(argv[optind], verbose, argc - optind - 1,
499d7f036beSkhorben &argv[optind + 1]);
500d7f036beSkhorben }
501