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