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