xref: /netbsd-src/sbin/umbctl/umbctl.c (revision 2478cc98bc46747b4f341949a08cc39431309b15)
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