Part 3 of the MBIM patch updates ifconfig(8).
Index: sbin/ifconfig/ifconfig.8
===================================================================
RCS file: /cvs/src/sbin/ifconfig/ifconfig.8,v
retrieving revision 1.267
diff -u -p -u -p -r1.267 ifconfig.8
--- sbin/ifconfig/ifconfig.8 6 Apr 2016 10:07:14 -0000 1.267
+++ sbin/ifconfig/ifconfig.8 23 May 2016 09:50:08 -0000
@@ -519,6 +519,8 @@ tunnel
.Xr vxlan 4 )
.It
.Xr vlan 4
+.It
+.Xr mbim 4
.El
.\" BRIDGE
.Sh BRIDGE
@@ -1645,6 +1647,71 @@ will be assigned 802.1Q tag 5.
Disassociate from the parent interface.
This breaks the link between the vlan interface and its parent,
clears its vlan tag, flags, and link address, and shuts the interface down.
+.El
+.\" MBIM
+.Sh MBIM
+.nr nS 1
+.Bk -words
+.Nm ifconfig
+.Ar mbim-interface
+.Op Cm pin Ar pin
+.Op Cm chgpin Ar oldpin Ar newpin
+.Op Cm puk Ar puk Ar newpin
+.Op Oo Fl Oc Ns Cm apn Ar apn
+.Op Oo Fl Oc Ns Cm class Ar class,class,...
+.Op Oo Fl Oc Ns Cm roaming
+.Ek
+.nr nS 0
+.Pp
+The following options are available for an
+.Xr mbim 4
+interface:
+.Bl -tag -width Ds
+.It Cm pin Ar pin
+Enter the PIN required to unlock the SIM card. Most SIM cards will not
+allow to establish a network association without providing a PIN.
+.It Cm chgpin Ar oldpin Ar newpin
+Permanently changes the PIN of the SIM card from the current value
+.Ar oldpin
+to
+.Ar newpin .
+.It Cm puk Ar puk Ar newpin
+Sets the PIN of the SIM card to
+.Ar newpin
+using the PUK
+.Ar puk
+to validate the request.
+.It Cm apn Ar apn
+Set the "Access Point Name" required by your network provider.
+.It Fl apn
+Clear the current "Access Point Name" value.
+.It Cm class
+List all available cell classes.
+.It Cm class Ar class,class,...
+Set the preferred cell classes. Apart from those listed by
+.Nm Cm class
+the following aliases can be used:
+.Ar 4G,
+.Ar 3G,
+and
+.Ar 2G.
+.It Fl class
+Clear any cell class preferences.
+.It Cm roaming
+Enable data roaming.
+.It Fl roaming
+Disable data roaming.
+.It Cm up
+As soon as the interface is marked as "up", the
+.Xr mbim 4
+device will try to establish a data connection with the service provider.
+.It Cm down
+Marking the interface as "down" will terminate any existing data connection
+and deregister with the service provider.
+.It Cm devinfo
+Provides some additional information about the
+.Xr mbim 4
+device, e.g. the IMEI number.
.El
.Sh EXAMPLES
Assign the
Index: sbin/ifconfig/ifconfig.c
===================================================================
RCS file: /cvs/src/sbin/ifconfig/ifconfig.c,v
retrieving revision 1.322
diff -u -p -u -p -r1.322 ifconfig.c
--- sbin/ifconfig/ifconfig.c 3 May 2016 17:52:33 -0000 1.322
+++ sbin/ifconfig/ifconfig.c 23 May 2016 09:50:08 -0000
@@ -107,6 +107,8 @@
#include <ifaddrs.h>
#include "brconfig.h"
+#include <dev/usb/mbim.h>
+#include <dev/usb/if_mbim.h>
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
@@ -143,8 +145,10 @@ int Lflag = 1;
int showmediaflag;
int showcapsflag;
+int showdevinfoflag;
int shownet80211chans;
int shownet80211nodes;
+int showclasses;
void notealias(const char *, int);
void setifaddr(const char *, int);
@@ -275,6 +279,19 @@ void unsetifdesc(const char *, int);
void printifhwfeatures(const char *, int);
void setpair(const char *, int);
void unsetpair(const char *, int);
+void mbim_status(void);
+void mbim_devinfo(const char *, int);
+void mbim_printclasses(char *, int);
+int mbim_parse_classes(const char *);
+void mbim_setpin(const char *, int);
+void mbim_chgpin(const char *, const char *);
+void mbim_puk(const char *, const char *);
+void mbim_pinop(int, int, const char *, const char *);
+void mbim_apn(const char *, int);
+void mbim_setclass(const char *, int);
+void mbim_roaming(const char *, int);
+void utf16_to_char(uint16_t *, int, char *, size_t);
+int char_to_utf16(const char *, uint16_t *, size_t);
#else
void setignore(const char *, int);
#endif
@@ -486,6 +503,16 @@ const struct cmd {
{ "-descr", 1, 0, unsetifdesc },
{ "wol", IFXF_WOL, 0, setifxflags },
{ "-wol", -IFXF_WOL, 0, setifxflags },
+ { "devinfo", NEXTARG0, 0, mbim_devinfo },
+ { "pin", NEXTARG, 0, mbim_setpin },
+ { "chgpin", NEXTARG2, 0, NULL, mbim_chgpin },
+ { "puk", NEXTARG2, 0, NULL, mbim_puk },
+ { "apn", NEXTARG, 0, mbim_apn },
+ { "-apn", -1, 0, mbim_apn },
+ { "class", NEXTARG0, 0, mbim_setclass },
+ { "-class", -1, 0, mbim_setclass },
+ { "roaming", 1, 0, mbim_roaming },
+ { "-roaming", 0, 0, mbim_roaming },
{ "patch", NEXTARG, 0, setpair },
{ "-patch", 1, 0, unsetpair },
#else /* SMALL */
@@ -2898,6 +2925,8 @@ status(int link, struct sockaddr_dl *sdl
#ifndef SMALL
if (showcapsflag)
printifhwfeatures(NULL, 1);
+ if (showdevinfoflag)
+ mbim_devinfo(NULL, 1);
#endif
if (sdl != NULL && sdl->sdl_alen &&
(sdl->sdl_type == IFT_ETHER || sdl->sdl_type == IFT_CARP))
@@ -2942,6 +2971,7 @@ status(int link, struct sockaddr_dl *sdl
mpe_status();
mpw_status();
pflow_status();
+ mbim_status();
#endif
trunk_status();
getifgroups();
@@ -4875,6 +4905,438 @@ setifpriority(const char *id, int param)
if (ioctl(s, SIOCSIFPRIORITY, (caddr_t)&ifr) < 0)
warn("SIOCSIFPRIORITY");
}
+
+
+const struct mbim_valdescr mbim_regstate[] = MBIM_REGSTATE_DESCRIPTIONS;
+const struct mbim_valdescr mbim_dataclass[] = MBIM_DATACLASS_DESCRIPTIONS;
+const struct mbim_valdescr mbim_simstate[] = MBIM_SIMSTATE_DESCRIPTIONS;
+const struct mbim_valdescr mbim_istate[] = MBIM_INTERNAL_STATE_DESCRIPTIONS;
+const struct mbim_valdescr mbim_pktstate[] = MBIM_PKTSRV_STATE_DESCRIPTIONS;
+const struct mbim_valdescr mbim_actstate[] =
MBIM_ACTIVATION_STATE_DESCRIPTIONS;
+
+const struct mbim_valdescr mbim_classalias[] = {
+ { MBIM_DATACLASS_GPRS | MBIM_DATACLASS_EDGE, "2g" },
+ { MBIM_DATACLASS_UMTS | MBIM_DATACLASS_HSDPA | MBIM_DATACLASS_HSUPA,
+ "3g" },
+ { MBIM_DATACLASS_LTE, "4g" },
+ { 0, NULL }
+};
+
+int
+mbim_descr2val(const struct mbim_valdescr *vdp, char *str)
+{
+ while (vdp->descr != NULL) {
+ if (!strcasecmp(vdp->descr, str))
+ return vdp->val;
+ vdp++;
+ }
+ return 0;
+}
+
+void
+mbim_status(void)
+{
+ struct mbim_info mi;
+ char provider[MBIM_PROVIDERNAME_MAXLEN+1];
+ char roamingtxt[MBIM_ROAMINGTEXT_MAXLEN+1];
+ int n;
+
+ memset((char *)&mi, 0, sizeof(mi));
+ ifr.ifr_data = (caddr_t)&mi;
+ if (ioctl(s, SIOCGMBIMINFO, (caddr_t)&ifr) == -1)
+ return;
+
+ if (mi.nwerror) {
+ /* 3GPP 24.008 Cause Code */
+ printf("\terror: ");
+ switch (mi.nwerror) {
+ case 2:
+ printf("SIM not activated");
+ break;
+ case 4:
+ printf("Roaming not supported");
+ break;
+ case 6:
+ printf("SIM reported stolen");
+ break;
+ case 7:
+ printf("No GPRS subscription");
+ break;
+ case 8:
+ printf("GPRS and non-GPRS services not allowed");
+ break;
+ case 11:
+ printf("Subscription expired");
+ break;
+ case 12:
+ printf("Subscription does not cover current location");
+ break;
+ case 13:
+ printf("No roaming in this location");
+ break;
+ case 14:
+ printf("GPRS not supported");
+ break;
+ case 15:
+ printf("No subscription for the service");
+ break;
+ case 17:
+ printf("Registration failed");
+ break;
+ case 22:
+ printf("Network congestion");
+ break;
+ default:
+ printf("Error code %d", mi.nwerror);
+ break;
+ }
+ printf("\n");
+ }
+
+ printf("\tstate: %s (%s, %s)\n", mbim_val2descr(mbim_istate, mi.state),
+ mbim_val2descr(mbim_pktstate, mi.packetstate),
+ mbim_val2descr(mbim_actstate, mi.activation));
+ printf("\tpin: ");
+ switch (mi.pin_state) {
+ case MBIM_PIN_REQUIRED:
+ printf("required");
+ break;
+ case MBIM_PIN_UNLOCKED:
+ printf("valid");
+ break;
+ case MBIM_PUK_REQUIRED:
+ printf("locked (PUK required)");
+ break;
+ default:
+ printf("unkown state (%d)", mi.pin_state);
+ break;
+ }
+ if (mi.pin_attempts_left != MBIM_VALUE_UNKNOWN)
+ printf(" (%d attempts left)", mi.pin_attempts_left);
+ printf("\n");
+ printf("\troaming: %s\n", mi.enable_roaming ? "enabled" : "disabled");
+ printf("\tregistration: %s",
+ mbim_val2descr(mbim_regstate, mi.regstate));
+ utf16_to_char(mi.roamingtxt, MBIM_ROAMINGTEXT_MAXLEN,
+ roamingtxt, sizeof (roamingtxt));
+ if (roamingtxt[0])
+ printf(" [%s]", roamingtxt);
+ printf("\n");
+
+ utf16_to_char(mi.provider, MBIM_PROVIDERNAME_MAXLEN,
+ provider, sizeof (provider));
+ if (provider[0])
+ printf("\tprovider: %s\n", provider);
+
+ if (showclasses)
+ mbim_printclasses("available classes", mi.supportedclasses);
+ if (mi.preferredclasses != MBIM_DATACLASS_NONE)
+ mbim_printclasses("preferred classes", mi.preferredclasses);
+ printf("\tcell class: %s\n",
+ mbim_val2descr(mbim_dataclass, mi.highestclass));
+
+ if (mi.rssi != MBIM_VALUE_UNKNOWN && mi.rssi != 0)
+ printf("\trssi: %ddBm\n", mi.rssi);
+ switch (mi.ber) {
+ case MBIM_BER_EXCELLENT:
+ printf("\tber: excellent\n");
+ break;
+ case MBIM_BER_VERYGOOD:
+ printf("\tber: very good\n");
+ break;
+ case MBIM_BER_GOOD:
+ printf("\tber: good\n");
+ break;
+ case MBIM_BER_OK:
+ printf("\tber: ok\n");
+ break;
+ case MBIM_BER_MEDIUM:
+ printf("\tber: medium\n");
+ break;
+ case MBIM_BER_BAD:
+ printf("\tber: bad\n");
+ break;
+ case MBIM_BER_VERYBAD:
+ printf("\tber: very bad\n");
+ break;
+ case MBIM_BER_EXTREMELYBAD:
+ printf("\tber: extremely bad\n");
+ break;
+ default:
+ break;
+ }
+ if (mi.uplink_speed != 0 || mi.downlink_speed != 0) {
+ char s[2][FMT_SCALED_STRSIZE];
+ if (fmt_scaled(mi.uplink_speed, s[0]) != 0)
+ snprintf(s[0], sizeof (s[0]), "%llu", mi.uplink_speed);
+ if (fmt_scaled(mi.downlink_speed, s[1]) != 0)
+ snprintf(s[1], sizeof (s[1]), "%llu",
mi.downlink_speed);
+ printf("\tspeed: %sps up %sps down\n", s[0], s[1]);
+ }
+ for (n = 0; n < MBIM_MAX_DNSSRV; n++) {
+ if (mi.ipv4dns[n] == INADDR_ANY)
+ break;
+ printf("\tdns%d: %s\n", n,
+ inet_ntoa(*(struct in_addr *)&mi.ipv4dns[n]));
+ }
+}
+
+void
+mbim_devinfo(const char *unused, int show)
+{
+ struct mbim_info mi;
+ char devid[MBIM_DEVID_MAXLEN+1];
+ char fwinfo[MBIM_FWINFO_MAXLEN+1];
+ char hwinfo[MBIM_HWINFO_MAXLEN+1];
+ char sid[MBIM_SUBSCRIBERID_MAXLEN+1];
+ char iccid[MBIM_ICCID_MAXLEN+1];
+ char apn[MBIM_APN_MAXLEN+1];
+ char pn[MBIM_PHONENR_MAXLEN+1];
+ int n;
+
+ if (!show) {
+ if (showdevinfoflag)
+ usage(1);
+ showdevinfoflag = 1;
+ return;
+ }
+
+ memset((char *)&mi, 0, sizeof(mi));
+ ifr.ifr_data = (caddr_t)&mi;
+ if (ioctl(s, SIOCGMBIMINFO, (caddr_t)&ifr) == -1)
+ return;
+
+ utf16_to_char(mi.devid, MBIM_DEVID_MAXLEN, devid, sizeof (devid));
+ if (devid[0]) {
+ switch (mi.cellclass) {
+ case MBIM_CELLCLASS_GSM:
+ printf("\tIMEI: ");
+ break;
+ case MBIM_CELLCLASS_CDMA:
+ n = strlen(devid);
+ if (n == 8 || n == 11) {
+ printf("\tESN: ");
+ break;
+ } else if (n == 14 || n == 18) {
+ printf("\tMEID: ");
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ printf("\tID: ");
+ break;
+ }
+ printf("%s\n", devid);
+ }
+ utf16_to_char(mi.hwinfo, MBIM_HWINFO_MAXLEN, hwinfo, sizeof (hwinfo));
+ if (hwinfo[0])
+ printf("\tdevice: %s\n", hwinfo);
+ utf16_to_char(mi.fwinfo, MBIM_FWINFO_MAXLEN, fwinfo, sizeof (fwinfo));
+ if (fwinfo[0])
+ printf("\tfirmware: %s\n", fwinfo);
+
+ utf16_to_char(mi.sid, MBIM_SUBSCRIBERID_MAXLEN, sid, sizeof (sid));
+ if (sid[0])
+ printf("\tsubscriber id: %s\n", sid);
+ utf16_to_char(mi.iccid, MBIM_ICCID_MAXLEN, iccid, sizeof (iccid));
+ if (iccid[0])
+ printf("\tICC id: %s\n", iccid);
+ printf("\tradio: hw %s, sw %s\n",
+ mi.hw_radio_on ? "on" : "off", mi.sw_radio_on ? "on" : "off");
+
+ printf("\tSIM: %s\n", mbim_val2descr(mbim_simstate, mi.sim_state));
+ utf16_to_char(mi.apn, MBIM_APN_MAXLEN, apn, sizeof (apn));
+ if (apn[0])
+ printf("\tAPN: %s\n", apn);
+ utf16_to_char(mi.pn, MBIM_PHONENR_MAXLEN, pn, sizeof (pn));
+ if (pn[0])
+ printf("\tphone number: +%s\n", pn);
+}
+
+void
+mbim_printclasses(char *tag, int c)
+{
+ int i;
+ char *sep = "";
+
+ printf("\t%s: ", tag);
+ i = 0;
+ while (mbim_dataclass[i].descr) {
+ if (mbim_dataclass[i].val & c) {
+ printf("%s%s", sep, mbim_dataclass[i].descr);
+ sep = ",";
+ }
+ i++;
+ }
+ printf("\n");
+}
+
+int
+mbim_parse_classes(const char *spec)
+{
+ char *optlist, *str;
+ int c = 0, v;
+
+ if ((optlist = strdup(spec)) == NULL)
+ err(1, "strdup");
+ str = strtok(optlist, ",");
+ while (str != NULL) {
+ if ((v = mbim_descr2val(mbim_dataclass, str)) != 0 ||
+ (v = mbim_descr2val(mbim_classalias, str)) != 0)
+ c |= v;
+ str = strtok(NULL, ",");
+ }
+ free(optlist);
+ return c;
+}
+
+void
+mbim_setpin(const char *pin, int d)
+{
+ mbim_pinop(MBIM_PIN_OP_ENTER, 0, pin, NULL);
+}
+
+void
+mbim_chgpin(const char *pin, const char *newpin)
+{
+ mbim_pinop(MBIM_PIN_OP_CHANGE, 0, pin, newpin);
+}
+
+void
+mbim_puk(const char *pin, const char *newpin)
+{
+ mbim_pinop(MBIM_PIN_OP_ENTER, 1, pin, newpin);
+}
+
+void
+mbim_pinop(int op, int is_puk, const char *pin, const char *newpin)
+{
+ struct mbim_parameter mp;
+
+ memset(&mp, 0, sizeof (mp));
+ ifr.ifr_data = (caddr_t)∓
+ if (ioctl(s, SIOCGMBIMPARAM, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGMBIMPARAM");
+
+ mp.op = op;
+ mp.is_puk = is_puk;
+ if ((mp.pinlen = char_to_utf16(pin, (uint16_t *)mp.pin,
+ sizeof (mp.pin))) == -1)
+ errx(1, "PIN too long");
+
+ if (newpin) {
+ if ((mp.newpinlen = char_to_utf16(newpin, (uint16_t *)mp.newpin,
+ sizeof (mp.newpin))) == -1)
+ errx(1, "new PIN too long");
+ }
+
+ if (ioctl(s, SIOCSMBIMPARAM, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSMBIMPARAM");
+}
+
+void
+mbim_apn(const char *apn, int d)
+{
+ struct mbim_parameter mp;
+
+ memset(&mp, 0, sizeof (mp));
+ ifr.ifr_data = (caddr_t)∓
+ if (ioctl(s, SIOCGMBIMPARAM, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGMBIMPARAM");
+
+ if (d != 0)
+ memset(mp.apn, 0, sizeof (mp.apn));
+ else if ((mp.apnlen = char_to_utf16(apn, mp.apn,
+ sizeof (mp.apn))) == -1)
+ errx(1, "APN too long");
+
+ if (ioctl(s, SIOCSMBIMPARAM, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSMBIMPARAM");
+}
+
+void
+mbim_setclass(const char *val, int d)
+{
+ struct mbim_parameter mp;
+
+ if (val == NULL) {
+ if (showclasses)
+ usage(1);
+ showclasses = 1;
+ return;
+ }
+
+ memset(&mp, 0, sizeof (mp));
+ ifr.ifr_data = (caddr_t)∓
+ if (ioctl(s, SIOCGMBIMPARAM, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGMBIMPARAM");
+ if (d != -1)
+ mp.preferredclasses = mbim_parse_classes(val);
+ else
+ mp.preferredclasses = MBIM_DATACLASS_NONE;
+ if (ioctl(s, SIOCSMBIMPARAM, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSMBIMPARAM");
+}
+
+void
+mbim_roaming(const char *val, int d)
+{
+ struct mbim_parameter mp;
+
+ memset(&mp, 0, sizeof (mp));
+ ifr.ifr_data = (caddr_t)∓
+ if (ioctl(s, SIOCGMBIMPARAM, (caddr_t)&ifr) == -1)
+ err(1, "SIOCGMBIMPARAM");
+ mp.roaming = d;
+ if (ioctl(s, SIOCSMBIMPARAM, (caddr_t)&ifr) == -1)
+ err(1, "SIOCSMBIMPARAM");
+}
+
+void
+utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen)
+{
+ uint16_t c;
+
+ while (outlen > 0) {
+ c = inlen > 0 ? letoh16(*in) : 0;
+ if (c == 0 || --outlen == 0) {
+ /* always NUL terminate result */
+done:
+ *out = '\0';
+ break;
+ }
+ *out++ = isascii(c) ? (char)c : '?';
+ in++;
+ inlen -= sizeof (*in);
+ }
+}
+
+int
+char_to_utf16(const char *in, uint16_t *out, size_t outlen)
+{
+ int n = 0;
+ uint16_t c;
+
+ for (;;) {
+ c = *in++;
+
+ if (c == '\0') {
+ /*
+ * NUL termination is not required, but zero out the
+ * residual buffer
+ */
+ memset(out, 0, outlen);
+ return n;
+ }
+ if (outlen < sizeof (*out))
+ return -1;
+
+ *out++ = htole16(c);
+ n += sizeof (*out);
+ outlen -= sizeof (*out);
+ }
+}
+
#endif
#define SIN(x) ((struct sockaddr_in *) &(x))