This is part 2 of the MBIM patch. It adds the mbim driver to i386
and amd64 kernels.


Index: sys/arch/amd64/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v
retrieving revision 1.418
diff -u -p -u -p -r1.418 GENERIC
--- sys/arch/amd64/conf/GENERIC 7 May 2016 23:10:50 -0000       1.418
+++ sys/arch/amd64/conf/GENERIC 23 May 2016 09:50:08 -0000
@@ -279,6 +279,7 @@ urtw*       at uhub?                # Realtek 8187
 rsu*   at uhub?                # Realtek RTL8188SU/RTL8191SU/RTL8192SU
 urtwn* at uhub?                # Realtek RTL8188CU/RTL8192CU
 udcf*  at uhub?                # Gude Expert mouseCLOCK
+mbim*  at uhub?                # Mobile Broadband Interface Model
 uthum* at uhidev?              # TEMPerHUM sensor
 ugold* at uhidev?              # gold TEMPer sensor
 utrh*  at uhidev?              # USBRH sensor
Index: sys/arch/i386/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/i386/conf/GENERIC,v
retrieving revision 1.814
diff -u -p -u -p -r1.814 GENERIC
--- sys/arch/i386/conf/GENERIC  24 Apr 2016 17:30:31 -0000      1.814
+++ sys/arch/i386/conf/GENERIC  23 May 2016 09:50:08 -0000
@@ -14,6 +14,9 @@ include               "../../../conf/GENERIC"
 maxusers       80                      # estimated number of users
 
 option         USER_PCICONF    # user-space PCI configuration
+option         USB_DEBUG
+#option                UHUB_DEBUG
+option         EHCI_DEBUG
 
 #option                VM86            # Virtual 8086 emulation
 option         KVM86           # Kernel Virtual 8086 emulation
@@ -314,6 +317,7 @@ rsu*        at uhub?                # Realtek 
RTL8188SU/RTL81
 urtwn* at uhub?                # Realtek RTL8188CU/RTL8192CU
 udcf*  at uhub?                # Gude Expert mouseCLOCK
 umbg*  at uhub?                # Meinberg Funkuhren USB5131
+mbim*  at uhub?                # Mobile Broadband Interface Model
 uthum* at uhidev?              # TEMPerHUM sensor
 ugold* at uhidev?              # gold TEMPer sensor
 utrh*  at uhidev?              # USBRH sensor
Index: sys/dev/usb/files.usb
===================================================================
RCS file: /cvs/src/sys/dev/usb/files.usb,v
retrieving revision 1.126
diff -u -p -u -p -r1.126 files.usb
--- sys/dev/usb/files.usb       8 Jan 2016 15:54:13 -0000       1.126
+++ sys/dev/usb/files.usb       23 May 2016 09:50:08 -0000
@@ -397,6 +397,11 @@ device     otus: ether, ifnet, ifmedia, wlan
 attach otus at uhub
 file   dev/usb/if_otus.c               otus
 
+# Mobile Broadband Interface Model
+device mbim: ifnet, ifmedia
+attach mbim at uhub
+file   dev/usb/if_mbim.c               mbim
+
 # USB logical device
 device usbf {}
 attach usbf at usbdev
Index: sys/dev/usb/if_mbim.c
===================================================================
RCS file: sys/dev/usb/if_mbim.c
diff -N sys/dev/usb/if_mbim.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/dev/usb/if_mbim.c       23 May 2016 09:50:08 -0000
@@ -0,0 +1,2487 @@
+/*     $OpenBSD$ */
+
+/*
+ * Copyright (c) 2016 genua mbH
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Mobile Broadband Interface Model
+ * http://www.usb.org/developers/docs/devclass_docs/MBIM-Compliance-1.0.pdf
+ */
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/time.h>
+#include <sys/timeout.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/if_arp.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+
+#include <machine/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/usbcdc.h>
+
+#include <dev/usb/mbim.h>
+#include <dev/usb/if_mbim.h>
+
+#ifdef MBIM_DEBUG
+#define DPRINTF(x...)                                                  \
+               do { if (mbim_debug) log(LOG_DEBUG, x); } while (0)
+
+#define DPRINTFN(n, x...)                                              \
+               do { if (mbim_debug >= (n)) log(LOG_DEBUG, x); } while (0)
+
+#define DDUMPN(n, b, l)                                                        
\
+               do {                                                    \
+                       if (mbim_debug >= (n))                          \
+                               mbim_dump((b), (l));                    \
+               } while (0)
+
+int     mbim_debug = 0;
+char   *mbim_uuid2str(uint8_t [MBIM_UUID_LEN]);
+void    mbim_dump(void *, int);
+
+#else
+#define DPRINTF(x...)          do { } while (0)
+#define DPRINTFN(n, x...)      do { } while (0)
+#define DDUMPN(n, b, l)                do { } while (0)
+#endif
+
+#define DEVNAM(sc)             (((struct mbim_softc *)(sc))->sc_dev.dv_xname)
+
+/*
+ * State change timeout
+ */
+#define MBIM_STATE_CHANGE_TIMEOUT      30
+
+/*
+ * State change flags
+ */
+#define MBIM_NS_DONT_DROP      0x0001  /* do not drop below current state */
+#define MBIM_NS_DONT_RAISE     0x0002  /* do not raise below current state */
+
+/*
+ * Diagnostic macros
+ */
+const struct mbim_valdescr mbim_regstates[] = MBIM_REGSTATE_DESCRIPTIONS;
+const struct mbim_valdescr mbim_dataclasses[] = MBIM_DATACLASS_DESCRIPTIONS;
+const struct mbim_valdescr mbim_simstate[] = MBIM_SIMSTATE_DESCRIPTIONS;
+const struct mbim_valdescr mbim_messages[] = MBIM_MESSAGES_DESCRIPTIONS;
+const struct mbim_valdescr mbim_status[] = MBIM_STATUS_DESCRIPTIONS;
+const struct mbim_valdescr mbim_cids[] = MBIM_CID_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_error[] = MBIM_ERROR_DESCRIPTIONS;
+const struct mbim_valdescr mbim_pintype[] = MBIM_PINTYPE_DESCRIPTIONS;
+const struct mbim_valdescr mbim_istate[] = MBIM_INTERNAL_STATE_DESCRIPTIONS;
+
+#define mbim_regstate(c)       mbim_val2descr(mbim_regstates, (c))
+#define mbim_dataclass(c)      mbim_val2descr(mbim_dataclasses, (c))
+#define mbim_simstate(s)       mbim_val2descr(mbim_simstate, (s))
+#define        mbim_request2str(m)     mbim_val2descr(mbim_messages, (m))
+#define        mbim_status2str(s)      mbim_val2descr(mbim_status, (s))
+#define        mbim_cid2str(c)         mbim_val2descr(mbim_cids, (c))
+#define        mbim_packet_state(s)    mbim_val2descr(mbim_pktstate, (s))
+#define        mbim_activation(s)      mbim_val2descr(mbim_actstate, (s))
+#define        mbim_error2str(e)       mbim_val2descr(mbim_error, (e))
+#define        mbim_pin_type(t)        mbim_val2descr(mbim_pintype, (t))
+#define        mbim_istate(s)          mbim_val2descr(mbim_istate, (s))
+
+/*
+ * Saved original interface address
+ * See "struct in_ifaddr".
+ */
+struct saved_ifaddr {
+       uint32_t                 netmask;
+       struct sockaddr_in       addr;
+       struct sockaddr_in       dst;
+       struct sockaddr_in       sockmask;
+};
+
+int             mbim_match(struct device *, void *, void *);
+void            mbim_attach(struct device *, struct device *, void *);
+int             mbim_detach(struct device *, int);
+int             mbim_activate(struct device *, int);
+int             mbim_alloc_xfers(struct mbim_softc *);
+void            mbim_free_xfers(struct mbim_softc *);
+int             mbim_alloc_bulkpipes(struct mbim_softc *);
+void            mbim_close_bulkpipes(struct mbim_softc *);
+int             mbim_ioctl(struct ifnet *, u_long, caddr_t);
+int             mbim_output(struct ifnet *, struct mbuf *, struct sockaddr *,
+                   struct rtentry *);
+int             mbim_input(struct ifnet *, struct mbuf *, void *);
+void            mbim_start(struct ifnet *);
+void            mbim_watchdog(struct ifnet *);
+void            mbim_statechg_timeout(void *);
+
+void            mbim_newstate(struct mbim_softc *, enum mbim_state, int);
+void            mbim_state_task(void *);
+void            mbim_up(struct mbim_softc *);
+void            mbim_down(struct mbim_softc *, int);
+void            mbim_linkstate(struct mbim_softc *, int);
+void            mbim_set_ipv4addr(struct ifnet *, struct in_ifaddr *, int);
+void            mbim_restore_ipv4addr(struct mbim_softc *);
+
+void            mbim_get_response_task(void *);
+
+void            mbim_decode_response(struct mbim_softc *, void *, int);
+void            mbim_handle_indicate_status_msg(struct mbim_softc *, void *,
+                   int);
+void            mbim_handle_opendone_msg(struct mbim_softc *, void *, int);
+void            mbim_handle_closedone_msg(struct mbim_softc *, void *, int);
+int             mbim_decode_register_state(struct mbim_softc *, void *, int);
+int             mbim_decode_devices_caps(struct mbim_softc *, void *, int);
+int             mbim_decode_subscriber_status(struct mbim_softc *, void *,
+                   int);
+int             mbim_decode_radio_state(struct mbim_softc *, void *, int);
+int             mbim_decode_pin(struct mbim_softc *, void *, int);
+int             mbim_decode_packet_service(struct mbim_softc *, void *, int);
+int             mbim_decode_signal_state(struct mbim_softc *, void *, int);
+int             mbim_decode_connect_info(struct mbim_softc *, void *, int);
+int             mbim_decode_ip_configuration(struct mbim_softc *, void *, int);
+void            mbim_update_gw(struct ifnet *);
+int             mbim_update_gw_walker(struct rtentry *, void *, unsigned int);
+
+void            mbim_rx(struct mbim_softc *);
+void            mbim_rxeof(struct usbd_xfer *, void *, usbd_status);
+int             mbim_encap(struct mbim_softc *, struct mbuf *);
+void            mbim_txeof(struct usbd_xfer *, void *, usbd_status);
+void            mbim_decap(struct mbim_softc *, struct usbd_xfer *);
+
+usbd_status     mbim_send_encap_command(struct mbim_softc *, void *, int);
+int             mbim_get_encap_response(struct mbim_softc *, void *, int *);
+void            mbim_ctrl_msg(struct mbim_softc *, uint32_t, void *, int);
+
+void            mbim_open(struct mbim_softc *);
+void            mbim_close(struct mbim_softc *);
+
+int             mbim_setpin(struct mbim_softc *, int, int, void *, int,
+                   void *, int);
+void            mbim_setdataclass(struct mbim_softc *);
+void            mbim_radio(struct mbim_softc *, int);
+void            mbim_packet_service(struct mbim_softc *, int);
+void            mbim_connect(struct mbim_softc *);
+void            mbim_disconnect(struct mbim_softc *);
+void            mbim_send_connect(struct mbim_softc *, int);
+
+void            mbim_qry_ipconfig(struct mbim_softc *);
+void            mbim_cmd(struct mbim_softc *, int, int, void *, int);
+void            mbim_command_done(struct mbim_softc *, void *, int);
+void            mbim_decode_cid(struct mbim_softc *, uint32_t, void *, int);
+
+void            mbim_intr(struct usbd_xfer *, void *, usbd_status);
+
+char           *mbim_ntop(struct sockaddr *);
+
+int             mbim_xfer_tout = USBD_DEFAULT_TIMEOUT;
+
+uint8_t                 mbim_uuid_basic_connect[] = MBIM_UUID_BASIC_CONNECT;
+uint8_t                 mbim_uuid_context_internet[] = 
MBIM_UUID_CONTEXT_INTERNET;
+uint32_t        mbim_session_id = 0;
+
+struct cfdriver mbim_cd = {
+       NULL, "mbim", DV_DULL
+};
+
+struct cfattach mbim_ca = {
+       sizeof (struct mbim_softc),
+       mbim_match,
+       mbim_attach,
+       mbim_detach,
+       mbim_activate,
+};
+
+/*
+ * Some devices are picky about too frequent control messages.
+ * Query device state not more often than every 0.5 secs.
+ */
+struct timeval mbim_update_rate = { 0, 500000 };
+int mbim_delay = 4000;
+
+/*
+ * Normally, MBIM devices are detected by their interface class and subclass.
+ * But for some models that have multiple configurations, it is better to
+ * match by vendor and product id so that we can select the desired
+ * configuration ourselves.
+ *
+ * OTOH, some devices identifiy themself als an MBIM device but fail to speak
+ * the MBIM protocol.
+ */
+struct mbim_products {
+       struct usb_devno         dev;
+       int                      confno;
+};
+const struct mbim_products mbim_devs[] = {
+       { { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC8305 }, 2 },
+       { { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM8805 }, 2 },
+};
+const struct usb_devno mbim_blacklist[] = {
+       /* Add blacklisted products here */
+};
+
+#define mbim_lookup(vid, pid)          \
+       ((const struct mbim_products *)usb_lookup(mbim_devs, vid, pid))
+
+int
+mbim_match(struct device *parent, void *match, void *aux)
+{
+       struct usb_attach_arg *uaa = aux;
+       usb_interface_descriptor_t *id;
+
+       if (usb_lookup(mbim_blacklist, uaa->vendor, uaa->product) != NULL)
+               return UMATCH_NONE;
+       if (mbim_lookup(uaa->vendor, uaa->product) != NULL)
+               return UMATCH_VENDOR_PRODUCT;
+       if (!uaa->iface)
+               return UMATCH_NONE;
+       if (!uaa->iface ||
+           (id = usbd_get_interface_descriptor(uaa->iface)) == NULL)
+               return UMATCH_NONE;
+       if (id->bInterfaceClass != UICLASS_CDC ||
+           id->bInterfaceSubClass !=
+           UISUBCLASS_MOBILE_BROADBAND_INTERFACE_MODEL ||
+           id->bNumEndpoints != 1)
+               return UMATCH_NONE;
+
+       return UMATCH_DEVCLASS_DEVSUBCLASS;
+}
+
+void
+mbim_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct mbim_softc *sc = (struct mbim_softc *)self;
+       struct usb_attach_arg *uaa = aux;
+       usbd_status status;
+       struct usbd_desc_iter iter;
+       const usb_descriptor_t *desc;
+       int      v;
+       struct mbim_descriptor *md;
+       int      i;
+       struct usbd_interface *ctrl_iface = NULL;
+       int      ctrl_ep;
+       uint8_t  data_ifaceno;
+       usb_interface_descriptor_t *id;
+       usb_config_descriptor_t *cd;
+       usb_endpoint_descriptor_t *ed;
+       int      s;
+       struct ifnet *ifp;
+       int      hard_mtu;
+
+       sc->sc_udev = uaa->device;
+
+       if (uaa->configno < 0) {
+               uaa->configno = mbim_lookup(uaa->vendor, uaa->product)->confno;
+               DPRINTF("%s: switching to config #%d\n", DEVNAM(sc),
+                   uaa->configno);
+               status = usbd_set_config_no(sc->sc_udev, uaa->configno, 1);
+               if (status) {
+                       printf("%s: failed to switch to config #%d: %s\n",
+                           DEVNAM(sc), uaa->configno, usbd_errstr(status));
+                       goto fail;
+               }
+       }
+
+       sc->sc_ver_maj = sc->sc_ver_min = -1;
+       usbd_desc_iter_init(sc->sc_udev, &iter);
+       hard_mtu = MBIM_MAXSEGSZ_MINVAL;
+       while ((desc = usbd_desc_iter_next(&iter))) {
+               if (desc->bDescriptorType != UDESC_CS_INTERFACE)
+                       continue;
+               switch (desc->bDescriptorSubtype) {
+               case UDESCSUB_MBIM:
+                       md = (struct mbim_descriptor *)desc;
+                       v = UGETW(md->bcdMBIMVersion);
+                       sc->sc_ver_maj = MBIM_VER_MAJOR(v);
+                       sc->sc_ver_min = MBIM_VER_MINOR(v);
+                       sc->sc_ctrl_len = UGETW(md->wMaxControlMessage);
+                       /* Never trust a USB device! Could try to exploit us */
+                       if (sc->sc_ctrl_len < MBIM_CTRLMSG_MINLEN ||
+                           sc->sc_ctrl_len > MBIM_CTRLMSG_MAXLEN) {
+                               printf("%s: control message len %d out of "
+                                   "bounds [%d .. %d]\n", DEVNAM(sc),
+                                   sc->sc_ctrl_len, MBIM_CTRLMSG_MINLEN,
+                                   MBIM_CTRLMSG_MAXLEN);
+                               /* cont. anyway */
+                       }
+                       sc->sc_maxpktlen = UGETW(md->wMaxSegmentSize);
+                       if (sc->sc_maxpktlen < MBIM_MAXSEGSZ_MINVAL) {
+                               printf("%s: ignoring invalid segment size %d\n",
+                                   DEVNAM(sc), sc->sc_maxpktlen);
+                               /* cont. anyway */
+                               sc->sc_maxpktlen = 8 * 1024;
+                       }
+                       hard_mtu = sc->sc_maxpktlen;
+                       DPRINTFN(2, "%s: ctrl_len=%d, maxpktlen=%d, cap=0x%x\n",
+                           DEVNAM(sc), sc->sc_ctrl_len, sc->sc_maxpktlen,
+                           md->bmNetworkCapabilities);
+                       break;
+               default:
+                       break;
+               }
+       }
+       cd = usbd_get_config_descriptor(sc->sc_udev);
+       if (sc->sc_ver_maj < 0) {
+               printf("%s: missing MBIM descriptor\n", DEVNAM(sc));
+               goto fail;
+       }
+
+       for (i = 0; i < sc->sc_udev->cdesc->bNumInterface; i++) {
+               if (usbd_iface_claimed(sc->sc_udev, i))
+                       continue;
+               id = usbd_get_interface_descriptor(&sc->sc_udev->ifaces[i]);
+               if (id == NULL)
+                       continue;
+               if (id->bInterfaceClass == UICLASS_CDC &&
+                   id->bInterfaceSubClass ==
+                   UISUBCLASS_MOBILE_BROADBAND_INTERFACE_MODEL) {
+                       ctrl_iface = &sc->sc_udev->ifaces[i];
+                       sc->sc_ctrl_ifaceno = id->bInterfaceNumber;
+                       usbd_claim_iface(sc->sc_udev, i);
+               } else if (id->bInterfaceClass == UICLASS_CDC_DATA &&
+                   id->bInterfaceSubClass == UISUBCLASS_DATA &&
+                   id->bInterfaceProtocol == UIPROTO_DATA_MBIM) {
+                       sc->sc_data_iface = &sc->sc_udev->ifaces[i];
+                       data_ifaceno = id->bInterfaceNumber;
+                       usbd_claim_iface(sc->sc_udev, i);
+               }
+       }
+       if (ctrl_iface == NULL) {
+               printf("%s: no control interface found\n", DEVNAM(sc));
+               return;
+       }
+       if (sc->sc_data_iface == NULL) {
+               printf("%s: no data interface found\n", DEVNAM(sc));
+               goto fail;
+       }
+
+       id = usbd_get_interface_descriptor(ctrl_iface);
+       ctrl_ep = -1;
+       for (i = 0; i < id->bNumEndpoints && ctrl_ep == -1; i++) {
+               ed = usbd_interface2endpoint_descriptor(ctrl_iface, i);
+               if (ed == NULL)
+                       break;
+               if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT &&
+                   UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+                       ctrl_ep = ed->bEndpointAddress;
+       }
+       if (ctrl_ep == -1) {
+               printf("%s: missing interrupt endpoint\n", DEVNAM(sc));
+               goto fail;
+       }
+
+       id = usbd_get_interface_descriptor(sc->sc_data_iface);
+       sc->sc_rx_ep = sc->sc_tx_ep = -1;
+       if ((status = usbd_set_interface(sc->sc_data_iface,
+           MBIM_INTERFACE_ALTSETTING))) {
+               printf("%s: select alt interface %d failed: %s\n",
+                   DEVNAM(sc), MBIM_INTERFACE_ALTSETTING, usbd_errstr(status));
+               goto fail;
+       }
+       id = usbd_get_interface_descriptor(sc->sc_data_iface);
+       for (i = 0; i < id->bNumEndpoints; i++) {
+               if ((ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface,
+                   i)) == NULL)
+                       break;
+               if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK &&
+                   UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN)
+                       sc->sc_rx_ep = ed->bEndpointAddress;
+               else if (UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK &&
+                   UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT)
+                       sc->sc_tx_ep = ed->bEndpointAddress;
+       }
+       if (sc->sc_rx_ep == -1 || sc->sc_tx_ep == -1) {
+               printf("%s: missing bulk endpoints\n", DEVNAM(sc));
+               goto fail;
+       }
+
+       DPRINTFN(2, "%s: ctrl-ifno#%d: ep-ctrl=%d, data-ifno#%d: ep-rx=%d, "
+           "ep-tx=%d\n", DEVNAM(sc), sc->sc_ctrl_ifaceno,
+           UE_GET_ADDR(ctrl_ep), data_ifaceno,
+           UE_GET_ADDR(sc->sc_rx_ep), UE_GET_ADDR(sc->sc_tx_ep));
+
+       usb_init_task(&sc->sc_mbim_task, mbim_state_task, sc,
+           USB_TASK_TYPE_GENERIC);
+       usb_init_task(&sc->sc_get_response_task, mbim_get_response_task, sc,
+           USB_TASK_TYPE_GENERIC);
+       timeout_set(&sc->sc_statechg_timer, mbim_statechg_timeout, sc);
+
+       if (usbd_open_pipe_intr(ctrl_iface, ctrl_ep, USBD_SHORT_XFER_OK,
+           &sc->sc_ctrl_pipe, sc, &sc->sc_intr_msg, sizeof (sc->sc_intr_msg),
+           mbim_intr, USBD_DEFAULT_INTERVAL)) {
+               printf("%s: failed to open control pipe\n", DEVNAM(sc));
+               goto fail;
+       }
+       sc->sc_resp_buf = malloc(sc->sc_ctrl_len, M_USBDEV, M_NOWAIT);
+       if (sc->sc_resp_buf == NULL) {
+               printf("%s: allocation of resp buffer failed\n", DEVNAM(sc));
+               goto fail;
+       }
+       sc->sc_ctrl_msg = malloc(sc->sc_ctrl_len, M_USBDEV, M_NOWAIT);
+       if (sc->sc_ctrl_msg == NULL) {
+               printf("%s: allocation of ctrl msg buffer failed\n",
+                   DEVNAM(sc));
+               goto fail;
+       }
+
+       sc->sc_info.regstate = MBIM_REGSTATE_UNKNOWN;
+       sc->sc_info.pin_attempts_left = MBIM_VALUE_UNKNOWN;
+       sc->sc_info.rssi = MBIM_VALUE_UNKNOWN;
+       sc->sc_info.ber = MBIM_VALUE_UNKNOWN;
+
+       s = splnet();
+       ifp = GET_IFP(sc);
+       ifp->if_flags = IFF_SIMPLEX | IFF_MULTICAST | IFF_POINTOPOINT;
+       ifp->if_ioctl = mbim_ioctl;
+       ifp->if_start = mbim_start;
+
+       ifp->if_watchdog = mbim_watchdog;
+       strlcpy(ifp->if_xname, DEVNAM(sc), IFNAMSIZ);
+       ifp->if_link_state = LINK_STATE_DOWN;
+
+       ifp->if_type = IFT_MBIM;
+       ifp->if_addrlen = 0;
+       ifp->if_hdrlen = sizeof (struct ncm_header16) +
+           sizeof (struct ncm_pointer16);
+       ifp->if_mtu = 1500;             /* use a common default */
+       ifp->if_hardmtu = hard_mtu;
+       ifp->if_output = mbim_output;
+       if_attach(ifp);
+       if_ih_insert(ifp, mbim_input, NULL);
+       if_alloc_sadl(ifp);
+       ifp->if_softc = sc;
+#if NBPFILTER > 0
+       bpfattach(&ifp->if_bpf, ifp, DLT_RAW, 0);
+#endif
+       /*
+        * Open the device now so that we are able to query device information.
+        * XXX maybe close when done?
+        */
+       mbim_open(sc);
+       splx(s);
+
+       printf("%s: vers %d.%d\n", DEVNAM(sc), sc->sc_ver_maj, sc->sc_ver_min);
+       return;
+
+fail:
+       usbd_deactivate(sc->sc_udev);
+       return;
+}
+
+int
+mbim_detach(struct device *self, int flags)
+{
+       struct mbim_softc *sc = (struct mbim_softc *)self;
+       struct ifnet *ifp = GET_IFP(sc);
+       int      s;
+
+       s = splnet();
+       if (ifp->if_flags & IFF_RUNNING)
+               mbim_down(sc, 1);
+       mbim_close(sc);
+
+       usb_rem_wait_task(sc->sc_udev, &sc->sc_get_response_task);
+       if (timeout_initialized(&sc->sc_statechg_timer))
+               timeout_del(&sc->sc_statechg_timer);
+       sc->sc_nresp = 0;
+       usb_rem_wait_task(sc->sc_udev, &sc->sc_mbim_task);
+       if (sc->sc_ctrl_pipe) {
+               usbd_close_pipe(sc->sc_ctrl_pipe);
+               sc->sc_ctrl_pipe = NULL;
+       }
+       if (sc->sc_ctrl_msg) {
+               free(sc->sc_ctrl_msg, M_USBDEV, sc->sc_ctrl_len);
+               sc->sc_ctrl_msg = NULL;
+       }
+       if (sc->sc_resp_buf) {
+               free(sc->sc_resp_buf, M_USBDEV, sc->sc_ctrl_len);
+               sc->sc_resp_buf = NULL;
+       }
+       if (sc->sc_saved_ifaddr != NULL &&
+           sc->sc_saved_ifaddr != MBIM_IFADDR_NONE)
+               free(sc->sc_saved_ifaddr, M_USBDEV,
+                   sizeof (struct saved_ifaddr));
+       sc->sc_saved_ifaddr = NULL;
+
+       if (ifp->if_softc != NULL) {
+               if_ih_remove(ifp, mbim_input, NULL);
+               if_detach(ifp);
+       }
+
+       splx(s);
+       return 0;
+}
+
+int
+mbim_activate(struct device *self, int act)
+{
+       struct mbim_softc *sc = (struct mbim_softc *)self;
+
+       switch (act) {
+       case DVACT_DEACTIVATE:
+               usbd_deactivate(sc->sc_udev);
+               break;
+       }
+       return 0;
+}
+
+int
+mbim_alloc_xfers(struct mbim_softc *sc)
+{
+       if (!sc->sc_rx_xfer) {
+               if ((sc->sc_rx_xfer = usbd_alloc_xfer(sc->sc_udev)) != NULL)
+                       sc->sc_rx_buf = usbd_alloc_buffer(sc->sc_rx_xfer,
+                           sc->sc_maxpktlen + MBIM_HDR32_LEN);
+       }
+       if (!sc->sc_tx_xfer) {
+               if ((sc->sc_tx_xfer = usbd_alloc_xfer(sc->sc_udev)) != NULL)
+                       sc->sc_tx_buf = usbd_alloc_buffer(sc->sc_tx_xfer,
+                           sc->sc_maxpktlen + MBIM_HDR16_LEN);
+       }
+       return (sc->sc_rx_buf && sc->sc_tx_buf) ? 1 : 0;
+}
+
+void
+mbim_free_xfers(struct mbim_softc *sc)
+{
+       if (sc->sc_rx_xfer) {
+               /* implicit usbd_free_buffer() */
+               usbd_free_xfer(sc->sc_rx_xfer);
+               sc->sc_rx_xfer = NULL;
+               sc->sc_rx_buf = NULL;
+       }
+       if (sc->sc_tx_xfer) {
+               usbd_free_xfer(sc->sc_tx_xfer);
+               sc->sc_tx_xfer = NULL;
+               sc->sc_tx_buf = NULL;
+       }
+       if (sc->sc_tx_m) {
+               m_freem(sc->sc_tx_m);
+               sc->sc_tx_m = NULL;
+       }
+}
+
+int
+mbim_alloc_bulkpipes(struct mbim_softc *sc)
+{
+       struct ifnet *ifp = GET_IFP(sc);
+
+       if (!(ifp->if_flags & IFF_RUNNING)) {
+               if (usbd_open_pipe(sc->sc_data_iface, sc->sc_rx_ep,
+                   USBD_EXCLUSIVE_USE, &sc->sc_rx_pipe))
+                       return 0;
+               if (usbd_open_pipe(sc->sc_data_iface, sc->sc_tx_ep,
+                   USBD_EXCLUSIVE_USE, &sc->sc_tx_pipe))
+                       return 0;
+
+               ifp->if_flags |= IFF_RUNNING;
+               ifp->if_flags &= ~IFF_OACTIVE;
+               mbim_rx(sc);
+       }
+       return 1;
+}
+
+void
+mbim_close_bulkpipes(struct mbim_softc *sc)
+{
+       struct ifnet *ifp = GET_IFP(sc);
+
+       ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+       ifp->if_timer = 0;
+       if (sc->sc_rx_pipe) {
+               usbd_close_pipe(sc->sc_rx_pipe);
+               sc->sc_rx_pipe = NULL;
+       }
+       if (sc->sc_tx_pipe) {
+               usbd_close_pipe(sc->sc_tx_pipe);
+               sc->sc_tx_pipe = NULL;
+       }
+}
+
+int
+mbim_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+       struct proc *p = curproc;
+       struct mbim_softc *sc = ifp->if_softc;
+       struct ifreq *ifr = (struct ifreq *)data;
+       int      s, error = 0;
+       struct mbim_parameter mp;
+
+       if (usbd_is_dying(sc->sc_udev))
+               return EIO;
+
+       s = splnet();
+       switch (cmd) {
+       case SIOCSIFADDR:
+               sc->sc_if.if_rtrequest = p2p_rtrequest;
+               break;
+       case SIOCSIFFLAGS:
+               usb_add_task(sc->sc_udev, &sc->sc_mbim_task);
+               break;
+       case SIOCGMBIMINFO:
+               error = copyout(&sc->sc_info, ifr->ifr_data,
+                   sizeof (sc->sc_info));
+               break;
+       case SIOCSMBIMPARAM:
+               if ((error = suser(p, 0)) != 0)
+                       break;
+               if ((error = copyin(ifr->ifr_data, &mp, sizeof (mp))) != 0)
+                       break;
+
+               if ((error = mbim_setpin(sc, mp.op, mp.is_puk,
+                   mp.pin, mp.pinlen, mp.newpin, mp.newpinlen)) != 0)
+                       break;
+
+               if (mp.apnlen < 0 || mp.apnlen > sizeof (sc->sc_info.apn)) {
+                       error = EINVAL;
+                       break;
+               }
+               sc->sc_roaming = mp.roaming ? 1 : 0;
+               memset(sc->sc_info.apn, 0, sizeof (sc->sc_info.apn));
+               memcpy(sc->sc_info.apn, mp.apn, mp.apnlen);
+               sc->sc_info.apnlen = mp.apnlen;
+               sc->sc_info.preferredclasses = mp.preferredclasses;
+               mbim_setdataclass(sc);
+               break;
+       case SIOCGMBIMPARAM:
+               memset(&mp, 0, sizeof (mp));
+               memcpy(mp.apn, sc->sc_info.apn, sc->sc_info.apnlen);
+               mp.apnlen = sc->sc_info.apnlen;
+               mp.roaming = sc->sc_roaming;
+               mp.preferredclasses = sc->sc_info.preferredclasses;
+               error = copyout(&mp, ifr->ifr_data, sizeof (mp));
+               break;
+       case SIOCSIFMTU:
+               /* Does this include the NCM headers and tail? */
+               if (ifr->ifr_mtu > ifp->if_hardmtu) {
+                       error = EINVAL;
+                       break;
+               }
+               ifp->if_mtu = ifr->ifr_mtu;
+               break;
+       case SIOCGIFMTU:
+               ifr->ifr_mtu = ifp->if_mtu;
+               break;
+       case SIOCGIFHARDMTU:
+               ifr->ifr_hardmtu = ifp->if_hardmtu;
+               break;
+       case SIOCAIFADDR:
+       case SIOCSIFDSTADDR:
+       case SIOCADDMULTI:
+       case SIOCDELMULTI:
+               break;
+       default:
+               error = ENOTTY;
+               break;
+       }
+       splx(s);
+       return error;
+}
+
+int
+mbim_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
+    struct rtentry *rtp)
+{
+       struct mbim_softc *sc = ifp->if_softc;
+
+       if (usbd_is_dying(sc->sc_udev) || !(ifp->if_flags & IFF_RUNNING)) {
+               m_freem(m);
+               return ENETDOWN;
+       }
+       return if_enqueue(ifp, m);
+}
+
+int
+mbim_input(struct ifnet *ifp, struct mbuf *m, void *cookie)
+{
+       struct niqueue *inq;
+       uint8_t ipv;
+
+       if (((ifp->if_flags & IFF_UP) == 0) || (m->m_flags & M_FILDROP) != 0) {
+               m_freem(m);
+               return 1;
+       }
+       if (m->m_pkthdr.len < sizeof (struct ip)) {
+               ifp->if_ierrors++;
+               DPRINTFN(4, "%s: dropping short packet (len %d)\n", __func__,
+                   m->m_pkthdr.len);
+               m_freem(m);
+               return 1;
+       }
+       m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
+       m_copydata(m, 0, sizeof (ipv), &ipv);
+       ipv >>= 4;
+
+       ifp->if_ibytes += m->m_pkthdr.len;
+       switch (ipv) {
+       case 4:
+               inq = &ipintrq;
+               break;
+       case 6:
+               inq = &ip6intrq;
+               break;
+       default:
+               ifp->if_ierrors++;
+               DPRINTFN(4, "%s: dropping packet with bad IP version (%d)\n",
+                   __func__, ipv);
+               m_freem(m);
+               return 1;
+       }
+       niq_enqueue(inq, m);
+       return 1;
+}
+
+void
+mbim_start(struct ifnet *ifp)
+{
+       struct mbim_softc *sc = ifp->if_softc;
+       struct mbuf *m_head = NULL;
+
+       if (usbd_is_dying(sc->sc_udev) ||
+           !(ifp->if_flags & IFF_RUNNING) ||
+           (ifp->if_flags & IFF_OACTIVE))
+               return;
+
+       m_head = ifq_deq_begin(&ifp->if_snd);
+       if (m_head == NULL)
+               return;
+
+       if (!mbim_encap(sc, m_head)) {
+               ifq_deq_rollback(&ifp->if_snd, m_head);
+               ifp->if_flags |= IFF_OACTIVE;
+               return;
+       }
+       ifq_deq_commit(&ifp->if_snd, m_head);
+
+#if NBPFILTER > 0
+       if (ifp->if_bpf)
+               bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
+#endif
+
+       ifp->if_flags |= IFF_OACTIVE;
+       ifp->if_timer = (2 * mbim_xfer_tout) / 1000;
+}
+
+void
+mbim_watchdog(struct ifnet *ifp)
+{
+       struct mbim_softc *sc = ifp->if_softc;
+
+       if (usbd_is_dying(sc->sc_udev))
+               return;
+
+       ifp->if_oerrors++;
+       log(LOG_WARNING, "%s: watchdog timeout\n", DEVNAM(sc));
+       return;
+}
+
+void
+mbim_statechg_timeout(void *arg)
+{
+       struct mbim_softc *sc = arg;
+
+       log(LOG_INFO, "%s: state change time out\n",DEVNAM(sc));
+       usb_add_task(sc->sc_udev, &sc->sc_mbim_task);
+}
+
+void
+mbim_newstate(struct mbim_softc *sc, enum mbim_state newstate, int flags)
+{
+       if (newstate == sc->sc_state)
+               return;
+       if (((flags & MBIM_NS_DONT_DROP) && newstate < sc->sc_state) ||
+           ((flags & MBIM_NS_DONT_RAISE) && newstate > sc->sc_state))
+               return;
+       log(LOG_DEBUG, "%s: state going %s from '%s' to '%s'\n", DEVNAM(sc),
+           newstate > sc->sc_state ? "up" : "down",
+           mbim_istate(sc->sc_state), mbim_istate(newstate));
+       sc->sc_state = newstate;
+       usb_add_task(sc->sc_udev, &sc->sc_mbim_task);
+}
+
+void
+mbim_state_task(void *arg)
+{
+       struct mbim_softc *sc = arg;
+       struct ifnet *ifp = GET_IFP(sc);
+
+       int      s;
+
+       s = splnet();
+       if (ifp->if_flags & IFF_UP)
+               mbim_up(sc);
+       else
+               mbim_down(sc, 0);
+       mbim_linkstate(sc,
+           sc->sc_state == MBIM_S_UP ? LINK_STATE_UP : LINK_STATE_DOWN);
+       splx(s);
+}
+
+
+void
+mbim_up(struct mbim_softc *sc)
+{
+       struct ifnet *ifp = GET_IFP(sc);
+
+       splassert(IPL_NET);
+
+       switch (sc->sc_state) {
+       case MBIM_S_DOWN:
+               DPRINTF("%s: init: opening ...\n", DEVNAM(sc));
+               mbim_open(sc);
+               break;
+       case MBIM_S_OPEN:
+               DPRINTF("%s: init: turning radio on ...\n", DEVNAM(sc));
+               mbim_radio(sc, 1);
+               break;
+       case MBIM_S_RADIO:
+               DPRINTF("%s: init: checking SIM state ...\n", DEVNAM(sc));
+               mbim_cmd(sc, MBIM_CID_SUBSCRIBER_READY_STATUS, MBIM_CMDOP_QRY,
+                   NULL, 0);
+               break;
+       case MBIM_S_SIMREADY:
+               DPRINTF("%s: init: attaching ...\n", DEVNAM(sc));
+               mbim_packet_service(sc, 1);
+               break;
+       case MBIM_S_ATTACHED:
+               sc->sc_tx_seq = 0;
+               if (!mbim_alloc_xfers(sc)) {
+                       mbim_free_xfers(sc);
+                       log(LOG_ERR, "%s: allocation of xfers failed\n",
+                           DEVNAM(sc));
+                       break;
+               }
+               DPRINTF("%s: init: connecting ...\n", DEVNAM(sc));
+               mbim_connect(sc);
+               break;
+       case MBIM_S_CONNECTED:
+               DPRINTF("%s: init: getting IP config ...\n", DEVNAM(sc));
+               mbim_qry_ipconfig(sc);
+               break;
+       case MBIM_S_UP:
+               DPRINTF("%s: init: reached state UP\n", DEVNAM(sc));
+               if (!mbim_alloc_bulkpipes(sc)) {
+                       log(LOG_ERR, "%s: opening bulk pipes failed\n",
+                           DEVNAM(sc));
+                       ifp->if_flags &= ~IFF_UP;
+                       mbim_down(sc, 1);
+               }
+               break;
+       }
+       if (sc->sc_state < MBIM_S_UP)
+               timeout_add_sec(&sc->sc_statechg_timer,
+                   MBIM_STATE_CHANGE_TIMEOUT);
+       else
+               timeout_del(&sc->sc_statechg_timer);
+       return;
+}
+
+void
+mbim_down(struct mbim_softc *sc, int force)
+{
+       splassert(IPL_NET);
+
+       mbim_close_bulkpipes(sc);
+       if (sc->sc_state < MBIM_S_CONNECTED)
+               mbim_free_xfers(sc);
+
+       switch (sc->sc_state) {
+       case MBIM_S_UP:
+       case MBIM_S_CONNECTED:
+               DPRINTF("%s: stop: disconnecting ...\n", DEVNAM(sc));
+               mbim_disconnect(sc);
+               if (!force)
+                       break;
+               /*FALLTHROUGH*/
+       case MBIM_S_ATTACHED:
+               DPRINTF("%s: stop: detaching ...\n", DEVNAM(sc));
+               mbim_packet_service(sc, 0);
+               if (!force)
+                       break;
+               /*FALLTHROUGH*/
+       case MBIM_S_SIMREADY:
+       case MBIM_S_RADIO:
+               DPRINTF("%s: stop: turning radio off ...\n", DEVNAM(sc));
+               mbim_radio(sc, 0);
+               if (!force)
+                       break;
+               /*FALLTHROUGH*/
+       case MBIM_S_OPEN:
+       case MBIM_S_DOWN:
+               /* Do not close the device */
+               DPRINTF("%s: stop: reached state DOWN\n", DEVNAM(sc));
+               break;
+       }
+       if (force)
+               sc->sc_state = MBIM_S_OPEN;
+
+       if (sc->sc_state > MBIM_S_OPEN)
+               timeout_add_sec(&sc->sc_statechg_timer,
+                   MBIM_STATE_CHANGE_TIMEOUT);
+       else
+               timeout_del(&sc->sc_statechg_timer);
+}
+
+void
+mbim_linkstate(struct mbim_softc *sc, int state)
+{
+       struct ifnet *ifp = GET_IFP(sc);
+       int      s;
+
+       s = splnet();
+       if (ifp->if_link_state != state) {
+               log(LOG_INFO, "%s: link state changed from %s to %s\n",
+                   DEVNAM(sc),
+                   LINK_STATE_IS_UP(ifp->if_link_state) ? "up" : "down",
+                   LINK_STATE_IS_UP(state) ? "up" : "down");
+               ifp->if_link_state = state;
+               if_link_state_change(ifp);
+               if (!LINK_STATE_IS_UP(state)) {
+                       memset(sc->sc_info.ipv4dns, 0,
+                           sizeof (sc->sc_info.ipv4dns));
+                       mbim_restore_ipv4addr(sc);
+               }
+       }
+       splx(s);
+}
+
+void
+mbim_set_ipv4addr(struct ifnet *ifp, struct in_ifaddr *ia, int newifaddr)
+{
+       struct sockaddr_in ifaddr;
+       int      error;
+
+       splassert(IPL_NET);
+       memset(&ifaddr, 0, sizeof (ifaddr));
+       ifaddr.sin_family = ia->ia_addr.sin_family;
+       ifaddr.sin_len = sizeof(ifaddr);
+       ifaddr.sin_addr.s_addr = ia->ia_addr.sin_addr.s_addr;
+
+       log(LOG_INFO, "%s: %s IPv4 addr is %s, mask %s, gateway %s\n",
+           DEVNAM(ifp->if_softc), newifaddr ? "new" : "changed",
+           mbim_ntop((struct sockaddr *)&ifaddr),
+           mbim_ntop((struct sockaddr *)&ia->ia_sockmask),
+           mbim_ntop((struct sockaddr *)&ia->ia_dstaddr));
+       if (!newifaddr)
+               in_ifscrub(ifp, ia);
+       if ((error = in_ifinit(ifp, ia, &ifaddr, newifaddr)) == 0)
+               dohooks(ifp->if_addrhooks, 0);
+       else
+               log(LOG_ERR, "%s: unable to set address, error %d\n",
+                   DEVNAM(ifp->if_softc), error);
+       mbim_update_gw(ifp);
+}
+
+void
+mbim_restore_ipv4addr(struct mbim_softc *sc)
+{
+       struct ifnet *ifp = GET_IFP(sc);
+       struct in_ifaddr *ia = NULL;
+       struct ifaddr   *ifa;
+
+       splassert(IPL_NET);
+       if (sc->sc_saved_ifaddr == NULL)
+               return;
+
+       TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
+               if (ifa->ifa_addr->sa_family == AF_INET) {
+                       if ((ia = ifatoia(ifa)) != NULL)
+                               break;
+               }
+       }
+       if (ia == NULL)
+               return;
+
+       if (sc->sc_saved_ifaddr == MBIM_IFADDR_NONE) {
+               log(LOG_DEBUG, "%s: purge IPv4 addr %s\n", DEVNAM(sc),
+                   mbim_ntop((struct sockaddr *)&ia->ia_addr));
+               in_purgeaddr(&ia->ia_ifa);
+       } else {
+               struct saved_ifaddr *sif = sc->sc_saved_ifaddr;
+
+               memcpy(&ia->ia_netmask, &sif->netmask, sizeof (ia->ia_netmask));
+               memcpy(&ia->ia_addr, &sif->addr, sizeof (ia->ia_addr));
+               memcpy(&ia->ia_dstaddr, &sif->dst, sizeof (ia->ia_dstaddr));
+               memcpy(&ia->ia_sockmask, &sif->sockmask,
+                   sizeof (ia->ia_sockmask));
+               mbim_set_ipv4addr(ifp, ia, 0);
+               free(sc->sc_saved_ifaddr, M_USBDEV,
+                  sizeof (struct saved_ifaddr));
+       }
+       sc->sc_saved_ifaddr = NULL;
+}
+
+void
+mbim_get_response_task(void *arg)
+{
+       struct mbim_softc *sc = arg;
+       int      len;
+       int      s;
+
+       /*
+        * Function is required to send on RESPONSE_AVAILABLE notification for
+        * each encapsulated response that is to be processed by the host.
+        * But of course, we can receive multiple notifications before the
+        * response task is run.
+        */
+       s = splusb();
+       while (sc->sc_nresp > 0) {
+               --sc->sc_nresp;
+               len = sc->sc_ctrl_len;
+               if (mbim_get_encap_response(sc, sc->sc_resp_buf, &len))
+                       mbim_decode_response(sc, sc->sc_resp_buf, len);
+       }
+       splx(s);
+}
+
+void
+mbim_decode_response(struct mbim_softc *sc, void *response, int len)
+{
+       struct mbim_msghdr *hdr = response;
+       struct mbim_fragmented_msg_hdr *fraghdr;
+       uint32_t type;
+       uint32_t tid;
+
+       DPRINTFN(3, "%s: got response: len %d\n", DEVNAM(sc), len);
+       DDUMPN(4, response, len);
+
+       if (len < sizeof (*hdr) || letoh32(hdr->len) != len) {
+               /*
+                * We should probably cancel a transaction, but since the
+                * message is too short, we cannot decode the transaction
+                * id (tid) and hence don't know, whom to cancel. Must wait
+                * for the timeout.
+                */
+               DPRINTF("%s: received short response (len %d)\n",
+                   DEVNAM(sc), len);
+               return;
+       }
+
+       /*
+        * XXX FIXME: if message is fragmented, store it until last frag
+        *      is received and then re-assemble all fragments.
+        */
+       type = letoh32(hdr->type);
+       tid = letoh32(hdr->tid);
+       switch (type) {
+       case MBIM_INDICATE_STATUS_MSG:
+       case MBIM_COMMAND_DONE:
+               fraghdr = response;
+               if (letoh32(fraghdr->frag.nfrag) != 1) {
+                       DPRINTF("%s: discarding fragmented messages\n",
+                           DEVNAM(sc));
+                       return;
+               }
+               break;
+       default:
+               break;
+       }
+
+       DPRINTF("%s: <- rcv %s (tid %u)\n", DEVNAM(sc), mbim_request2str(type),
+           tid);
+       switch (type) {
+       case MBIM_FUNCTION_ERROR_MSG:
+       case MBIM_HOST_ERROR_MSG:
+       {
+               struct mbim_f2h_hosterr *e;
+               int      err;
+
+               if (len >= sizeof (*e)) {
+                       e = response;
+                       err = letoh32(e->err);
+
+                       DPRINTF("%s: %s message, error %s (tid %u)\n",
+                           DEVNAM(sc), mbim_request2str(type),
+                           mbim_error2str(err), tid);
+                       if (err == MBIM_ERROR_NOT_OPENED)
+                               mbim_newstate(sc, MBIM_S_DOWN, 0);
+               }
+               break;
+       }
+       case MBIM_INDICATE_STATUS_MSG:
+               mbim_handle_indicate_status_msg(sc, response, len);
+               break;
+       case MBIM_OPEN_DONE:
+               mbim_handle_opendone_msg(sc, response, len);
+               break;
+       case MBIM_CLOSE_DONE:
+               mbim_handle_closedone_msg(sc, response, len);
+               break;
+       case MBIM_COMMAND_DONE:
+               mbim_command_done(sc, response, len);
+               break;
+       default:
+               DPRINTF("%s: discard messsage %s\n", DEVNAM(sc),
+                   mbim_request2str(type));
+               break;
+       }
+}
+
+void
+mbim_handle_indicate_status_msg(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_f2h_indicate_status *m = data;
+       uint32_t infolen;
+       uint32_t cid;
+
+       if (len < sizeof (*m)) {
+               DPRINTF("%s: discard short %s messsage\n", DEVNAM(sc),
+                   mbim_request2str(letoh32(m->hdr.type)));
+               return;
+       }
+       if (memcmp(m->devid, mbim_uuid_basic_connect, sizeof (m->devid))) {
+               DPRINTF("%s: discard %s messsage for other UUID '%s'\n",
+                   DEVNAM(sc), mbim_request2str(letoh32(m->hdr.type)),
+                   mbim_uuid2str(m->devid));
+               return;
+       }
+       infolen = letoh32(m->infolen);
+       if (len < sizeof (*m) + infolen) {
+               DPRINTF("%s: discard truncated %s messsage (want %d, got %d)\n",
+                   DEVNAM(sc), mbim_request2str(letoh32(m->hdr.type)),
+                   (int)sizeof (*m) + infolen, len);
+               return;
+       }
+
+       cid = letoh32(m->cid);
+       DPRINTF("%s: indicate %s status\n", DEVNAM(sc), mbim_cid2str(cid));
+       mbim_decode_cid(sc, cid, m->info, infolen);
+}
+
+void
+mbim_handle_opendone_msg(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_f2h_openclosedone *resp = data;
+       uint32_t status;
+
+       status = letoh32(resp->status);
+       if (status == MBIM_STATUS_SUCCESS) {
+               if (sc->sc_maxsessions == 0) {
+                       mbim_cmd(sc, MBIM_CID_DEVICE_CAPS, MBIM_CMDOP_QRY, NULL,
+                           0);
+                       mbim_cmd(sc, MBIM_CID_PIN, MBIM_CMDOP_QRY, NULL, 0);
+                       mbim_cmd(sc, MBIM_CID_REGISTER_STATE, MBIM_CMDOP_QRY,
+                           NULL, 0);
+               }
+               mbim_newstate(sc, MBIM_S_OPEN, MBIM_NS_DONT_DROP);
+       } else
+               log(LOG_ERR, "%s: open error: %s\n", DEVNAM(sc),
+                   mbim_status2str(status));
+       return;
+}
+
+void
+mbim_handle_closedone_msg(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_f2h_openclosedone *resp = data;
+       uint32_t status;
+
+       status = letoh32(resp->status);
+       if (status == MBIM_STATUS_SUCCESS)
+               mbim_newstate(sc, MBIM_S_DOWN, 0);
+       else
+               DPRINTF("%s: close error: %s\n", DEVNAM(sc),
+                   mbim_status2str(status));
+       return;
+}
+
+static inline void
+mbim_getinfobuf(void *in, int inlen, uint32_t offs, uint32_t sz,
+    void *out, size_t outlen)
+{
+       offs = letoh32(offs);
+       sz = letoh32(sz);
+       if (inlen >= offs + sz) {
+               memset(out, 0, outlen);
+               memcpy(out, in + offs, MIN(sz, outlen));
+       }
+}
+
+static inline int
+mbim_padding(void *data, int len, size_t sz)
+{
+       char    *p = data;
+       int      np = 0;
+
+       while (len < sz && (len % 4) != 0) {
+               *p++ = '\0';
+               len++;
+               np++;
+       }
+       return np;
+}
+
+static inline int
+mbim_addstr(void *buf, size_t bufsz, int *offs, void *str, int slen,
+    uint32_t *offsmember, uint32_t *sizemember)
+{
+       if (*offs + slen > bufsz)
+               return 0;
+
+       *sizemember = htole32((uint32_t)slen);
+       if (slen && str) {
+               *offsmember = htole32((uint32_t)*offs);
+               memcpy(buf + *offs, str, slen);
+               *offs += slen;
+               *offs += mbim_padding(buf, *offs, bufsz);
+       } else
+               *offsmember = htole32(0);
+       return 1;
+}
+
+int
+mbim_decode_register_state(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_cid_registration_state_info *rs = data;
+
+       if (len < sizeof (*rs))
+               return 0;
+       sc->sc_info.nwerror = letoh32(rs->nwerror);
+       sc->sc_info.regstate = letoh32(rs->regstate);
+       sc->sc_info.regmode = letoh32(rs->regmode);
+       sc->sc_info.cellclass = letoh32(rs->curcellclass);
+
+       /* XXX should we remember the provider_id? */
+       mbim_getinfobuf(data, len, rs->provname_offs, rs->provname_size,
+           sc->sc_info.provider, sizeof (sc->sc_info.provider));
+       mbim_getinfobuf(data, len, rs->roamingtxt_offs, rs->roamingtxt_size,
+           sc->sc_info.roamingtxt, sizeof (sc->sc_info.roamingtxt));
+
+       DPRINTFN(2, "%s: %s, availclass 0x%x, class 0x%x, regmode %d\n",
+           DEVNAM(sc), mbim_regstate(sc->sc_info.regstate),
+           letoh32(rs->availclasses), sc->sc_info.cellclass,
+           sc->sc_info.regmode);
+
+       if (sc->sc_info.regstate == MBIM_REGSTATE_ROAMING &&
+           !sc->sc_roaming &&
+           sc->sc_info.activation == MBIM_ACTIVATION_STATE_ACTIVATED) {
+               log(LOG_INFO, "%s: disconnecting from roaming network\n",
+                   DEVNAM(sc));
+               mbim_newstate(sc, MBIM_S_ATTACHED, MBIM_NS_DONT_RAISE);
+       }
+       return 1;
+}
+
+int
+mbim_decode_devices_caps(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_cid_device_caps *dc = data;
+
+       if (len < sizeof (*dc))
+               return 0;
+       sc->sc_maxsessions = letoh32(dc->max_sessions);
+       sc->sc_info.supportedclasses = letoh32(dc->dataclass);
+       mbim_getinfobuf(data, len, dc->devid_offs, dc->devid_size,
+           sc->sc_info.devid, sizeof (sc->sc_info.devid));
+       mbim_getinfobuf(data, len, dc->fwinfo_offs, dc->fwinfo_size,
+           sc->sc_info.fwinfo, sizeof (sc->sc_info.fwinfo));
+       mbim_getinfobuf(data, len, dc->hwinfo_offs, dc->hwinfo_size,
+           sc->sc_info.hwinfo, sizeof (sc->sc_info.hwinfo));
+       DPRINTFN(2, "%s: max sessions %d, supported classes 0x%x\n",
+           DEVNAM(sc), sc->sc_maxsessions, sc->sc_info.supportedclasses);
+       return 1;
+}
+
+int
+mbim_decode_subscriber_status(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_cid_subscriber_ready_info *si = data;
+       int     npn;
+
+       if (len < sizeof (*si))
+               return 0;
+       sc->sc_info.sim_state = letoh32(si->ready);
+
+       mbim_getinfobuf(data, len, si->sid_offs, si->sid_size,
+           sc->sc_info.sid, sizeof (sc->sc_info.sid));
+       mbim_getinfobuf(data, len, si->icc_offs, si->icc_size,
+           sc->sc_info.iccid, sizeof (sc->sc_info.iccid));
+
+       npn = letoh32(si->no_pn);
+       if (npn > 0)
+               mbim_getinfobuf(data, len, si->pn[0].offs, si->pn[0].size,
+                   sc->sc_info.pn, sizeof (sc->sc_info.pn));
+       else
+               memset(sc->sc_info.pn, 0, sizeof (sc->sc_info.pn));
+
+       if (sc->sc_info.sim_state == MBIM_SIMSTATE_LOCKED)
+               sc->sc_info.pin_state = MBIM_PUK_REQUIRED;
+       log(LOG_INFO, "%s: SIM %s\n", DEVNAM(sc),
+           mbim_simstate(sc->sc_info.sim_state));
+       if (sc->sc_info.sim_state == MBIM_SIMSTATE_INITIALIZED)
+               mbim_newstate(sc, MBIM_S_SIMREADY, MBIM_NS_DONT_DROP);
+       return 1;
+}
+
+int
+mbim_decode_radio_state(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_cid_radio_state_info *rs = data;
+
+       if (len < sizeof (*rs))
+               return 0;
+
+       sc->sc_info.hw_radio_on =
+           (letoh32(rs->hw_state) == MBIM_RADIO_STATE_ON) ? 1 : 0;
+       sc->sc_info.sw_radio_on =
+           (letoh32(rs->sw_state) == MBIM_RADIO_STATE_ON) ? 1 : 0;
+       if (!sc->sc_info.hw_radio_on) {
+               log(LOG_INFO, "%s: radio is off by rfkill switch\n",
+                   DEVNAM(sc));
+               /*
+                * XXX do we need a time to poll the state of the rfkill switch
+                *      or will the device send an unsolicited notification
+                *      in case the state changes?
+                */
+               mbim_newstate(sc, MBIM_S_OPEN, 0);
+       } else if (!sc->sc_info.sw_radio_on) {
+               log(LOG_INFO, "%s: radio is off\n", DEVNAM(sc));
+               mbim_newstate(sc, MBIM_S_OPEN, 0);
+       } else
+               mbim_newstate(sc, MBIM_S_RADIO, MBIM_NS_DONT_DROP);
+       return 1;
+}
+
+int
+mbim_decode_pin(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_cid_pin_info *pi = data;
+       uint32_t        attempts_left;
+
+       if (len < sizeof (*pi))
+               return 0;
+
+       attempts_left = letoh32(pi->remaining_attempts);
+       if (attempts_left != 0xffffffff)
+               sc->sc_info.pin_attempts_left = attempts_left;
+
+       switch (letoh32(pi->state)) {
+       case MBIM_PIN_STATE_UNLOCKED:
+               sc->sc_info.pin_state = MBIM_PIN_UNLOCKED;
+               break;
+       case MBIM_PIN_STATE_LOCKED:
+               switch (letoh32(pi->type)) {
+               case MBIM_PIN_TYPE_PIN1:
+                       sc->sc_info.pin_state = MBIM_PIN_REQUIRED;
+                       break;
+               case MBIM_PIN_TYPE_PUK1:
+                       sc->sc_info.pin_state = MBIM_PUK_REQUIRED;
+                       break;
+               case MBIM_PIN_TYPE_PIN2:
+               case MBIM_PIN_TYPE_PUK2:
+                       /* Assume that PIN1 was accepted */
+                       sc->sc_info.pin_state = MBIM_PIN_UNLOCKED;
+                       break;
+               }
+               break;
+       }
+       log(LOG_INFO, "%s: %s state %s (%d attempts left)\n",
+           DEVNAM(sc), mbim_pin_type(letoh32(pi->type)),
+           (letoh32(pi->state) == MBIM_PIN_STATE_UNLOCKED) ?
+               "unlocked" : "locked",
+           letoh32(pi->remaining_attempts));
+
+       /*
+        * In case the PIN was set after IFF_UP, retrigger the state machine
+        */
+       usb_add_task(sc->sc_udev, &sc->sc_mbim_task);
+       return 1;
+}
+
+int
+mbim_decode_packet_service(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_cid_packet_service_info *psi = data;
+       int      state, highestclass;
+       uint64_t up_speed, down_speed;
+       struct ifnet *ifp = GET_IFP(sc);
+
+       if (len < sizeof (*psi))
+               return 0;
+
+       sc->sc_info.nwerror = letoh32(psi->nwerror);
+       state = letoh32(psi->state);
+       highestclass = letoh32(psi->highest_dataclass);
+       up_speed = letoh64(psi->uplink_speed);
+       down_speed = letoh64(psi->downlink_speed);
+       if (sc->sc_info.packetstate  != state ||
+           sc->sc_info.uplink_speed != up_speed ||
+           sc->sc_info.downlink_speed != down_speed) {
+               log(LOG_INFO, "%s: packet service ", DEVNAM(sc));
+               if (sc->sc_info.packetstate  != state)
+                       addlog("changed from %s to ",
+                           mbim_packet_state(sc->sc_info.packetstate));
+               addlog("%s, class %s, speed: %llu up / %llu down\n",
+                   mbim_packet_state(state), mbim_dataclass(highestclass),
+                   up_speed, down_speed);
+       }
+       sc->sc_info.packetstate = state;
+       sc->sc_info.highestclass = highestclass;
+       sc->sc_info.uplink_speed = up_speed;
+       sc->sc_info.downlink_speed = down_speed;
+
+       if (sc->sc_info.regmode == MBIM_REGMODE_AUTOMATIC) {
+               /*
+                * For devices using automatic registration mode, just proceed,
+                * once registration has completed.
+                */
+               if (ifp->if_flags & IFF_UP) {
+                       switch (sc->sc_info.regstate) {
+                       case MBIM_REGSTATE_HOME:
+                       case MBIM_REGSTATE_ROAMING:
+                       case MBIM_REGSTATE_PARTNER:
+                               mbim_newstate(sc, MBIM_S_ATTACHED,
+                                   MBIM_NS_DONT_DROP);
+                               break;
+                       default:
+                               break;
+                       }
+               } else
+                       mbim_newstate(sc, MBIM_S_SIMREADY, MBIM_NS_DONT_RAISE);
+       } else switch (sc->sc_info.packetstate) {
+       case MBIM_PKTSERVICE_STATE_ATTACHED:
+               mbim_newstate(sc, MBIM_S_ATTACHED, MBIM_NS_DONT_DROP);
+               break;
+       case MBIM_PKTSERVICE_STATE_DETACHED:
+               mbim_newstate(sc, MBIM_S_SIMREADY, MBIM_NS_DONT_RAISE);
+               break;
+       }
+       return 1;
+}
+
+int
+mbim_decode_signal_state(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_cid_signal_state *ss = data;
+       int      rssi;
+
+       if (len < sizeof (*ss))
+               return 0;
+
+       if (letoh32(ss->rssi) == 99)
+               rssi = MBIM_VALUE_UNKNOWN;
+       else {
+               rssi = -113 + 2 * letoh32(ss->rssi);
+               if (sc->sc_info.rssi != rssi)
+                       log(LOG_INFO, "%s: rssi %d dBm\n", DEVNAM(sc), rssi);
+       }
+       sc->sc_info.rssi = rssi;
+       sc->sc_info.ber = letoh32(ss->err_rate);
+       if (sc->sc_info.ber == -99)
+               sc->sc_info.ber = MBIM_VALUE_UNKNOWN;
+       return 1;
+}
+
+int
+mbim_decode_connect_info(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_cid_connect_info *ci = data;
+       int      act;
+
+       if (len < sizeof (*ci))
+               return 0;
+
+       if (letoh32(ci->sessionid) != mbim_session_id) {
+               DPRINTF("%s: discard connection info for session %u\n",
+                   DEVNAM(sc), letoh32(ci->sessionid));
+               return 1;
+       }
+       if (memcmp(ci->context, mbim_uuid_context_internet,
+           sizeof (ci->context))) {
+               DPRINTF("%s: discard connection info for other context\n",
+                   DEVNAM(sc));
+               return 1;
+       }
+       act = letoh32(ci->activation);
+       if (sc->sc_info.activation != act) {
+               log(LOG_INFO, "%s: connection %s\n", DEVNAM(sc),
+                   mbim_activation(act));
+               if (letoh32(ci->iptype) != MBIM_CONTEXT_IPTYPE_DEFAULT &&
+                   letoh32(ci->iptype) != MBIM_CONTEXT_IPTYPE_IPV4)
+                       log(LOG_DEBUG, "%s: got iptype %d connection\n",
+                           DEVNAM(sc), letoh32(ci->iptype));
+
+               sc->sc_info.activation = act;
+               sc->sc_info.nwerror = letoh32(ci->nwerror);
+
+               if (sc->sc_info.activation == MBIM_ACTIVATION_STATE_ACTIVATED)
+                       mbim_newstate(sc, MBIM_S_CONNECTED, MBIM_NS_DONT_DROP);
+               else if (sc->sc_info.activation ==
+                   MBIM_ACTIVATION_STATE_DEACTIVATED)
+                       mbim_newstate(sc, MBIM_S_ATTACHED, 0);
+               /* else: other states are purely transitional */
+       }
+       return 1;
+}
+
+int
+mbim_decode_ip_configuration(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_cid_ip_configuration_info *ic = data;
+       struct ifnet *ifp = GET_IFP(sc);
+       int      s;
+       uint32_t avail;
+       uint32_t val;
+       int      n, i;
+       int      off;
+       struct mbim_cid_ipv4_element ipv4elem;
+       struct in_ifaddr *ia = NULL;
+       struct ifaddr   *ifa;
+       int      newifaddr = 0;
+       int      ifaddr_changed = 0;
+       int      state = -1;
+
+       if (len < sizeof (*ic))
+               return 0;
+       if (letoh32(ic->sessionid) != mbim_session_id) {
+               DPRINTF("%s: ignore IP configration for session id %d\n",
+                   DEVNAM(sc), letoh32(ic->sessionid));
+               return 0;
+       }
+       s = splnet();
+
+       /*
+        * IPv4 configuation
+        */
+       TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
+               if (ifa->ifa_addr->sa_family == AF_INET) {
+                       if ((ia = ifatoia(ifa)) != NULL)
+                               break;
+               }
+       }
+
+       avail = letoh32(ic->ipv4_available);
+       if (avail & MBIM_IPCONF_HAS_ADDRINFO) {
+               n = letoh32(ic->ipv4_naddr);
+               off = letoh32(ic->ipv4_addroffs);
+
+               if (n == 0 || off + sizeof (ipv4elem) > len)
+                       goto done;
+
+               /* Only pick the first one */
+               memcpy(&ipv4elem, data + off, sizeof (ipv4elem));
+               ipv4elem.addr = letoh32(ipv4elem.addr);
+               ipv4elem.prefixlen = letoh32(ipv4elem.prefixlen);
+               if (ia == NULL) {
+                       ia = malloc(sizeof (*ia), M_IFADDR, M_WAITOK | M_ZERO);
+                       ia->ia_ifa.ifa_ifp = ifp;
+                       newifaddr = 1;
+                       /* No previous addr */
+                       sc->sc_saved_ifaddr = MBIM_IFADDR_NONE;
+               } else if (memcmp(&ipv4elem.addr, &ia->ia_addr.sin_addr.s_addr,
+                   sizeof (&ipv4elem.addr))) {
+                       ifaddr_changed = 1;
+
+                       /*
+                        * First time here, save original settings so we can
+                        * restore them later, when link goes down.
+                        */
+                       if (sc->sc_saved_ifaddr == NULL) {
+                               struct saved_ifaddr *sif;
+
+                               sif = malloc(sizeof (*sif), M_USBDEV,
+                                   M_WAITOK | M_ZERO);
+                               memcpy(&sif->netmask, &ia->ia_netmask,
+                                   sizeof (sif->netmask));
+                               memcpy(&sif->addr, &ia->ia_addr,
+                                   sizeof (sif->addr));
+                               memcpy(&sif->dst, &ia->ia_dstaddr,
+                                   sizeof (sif->dst));
+                               memcpy(&sif->sockmask, &ia->ia_sockmask,
+                                   sizeof (sif->sockmask));
+                               sc->sc_saved_ifaddr = sif;
+                       }
+               }
+               state = MBIM_S_UP;
+       } else if (ia != NULL) {
+               /* IP config has no address, but our interface has one. */
+               state = MBIM_S_CONNECTED;
+       }
+       if (newifaddr || ifaddr_changed) {
+               ia->ia_addr.sin_family = AF_INET;
+               ia->ia_addr.sin_len = sizeof(ia->ia_addr);
+               ia->ia_addr.sin_addr.s_addr = ipv4elem.addr;
+               ia->ia_dstaddr.sin_family = AF_INET;
+               ia->ia_dstaddr.sin_len = sizeof(ia->ia_addr);
+               if (avail & MBIM_IPCONF_HAS_GWINFO) {
+                       off = letoh32(ic->ipv4_gwoffs);
+                       ia->ia_dstaddr.sin_addr.s_addr =
+                           letoh32(*((uint32_t *)(data + off)));
+               } else if (newifaddr)
+                       ia->ia_dstaddr.sin_addr.s_addr = INADDR_BROADCAST;
+               ia->ia_sockmask.sin_family = AF_INET;
+               ia->ia_sockmask.sin_len = sizeof(ia->ia_addr);
+               in_len2mask(&ia->ia_sockmask.sin_addr, ipv4elem.prefixlen);
+               ia->ia_netmask = ia->ia_sockmask.sin_addr.s_addr;
+               ia->ia_ifa.ifa_addr = sintosa(&ia->ia_addr);
+               ia->ia_ifa.ifa_dstaddr = sintosa(&ia->ia_dstaddr);
+               ia->ia_ifa.ifa_netmask = sintosa(&ia->ia_sockmask);
+               mbim_set_ipv4addr(ifp, ia, newifaddr);
+       }
+
+       memset(sc->sc_info.ipv4dns, 0, sizeof (sc->sc_info.ipv4dns));
+       if ((avail & MBIM_IPCONF_HAS_DNSINFO) && ia != NULL) {
+               n = letoh32(ic->ipv4_ndnssrv);
+               off = letoh32(ic->ipv4_dnssrvoffs);
+               i = 0;
+               while (n-- > 0) {
+                       if (off + sizeof (uint32_t) > len)
+                               break;
+                       val = letoh32(*((uint32_t *)(data + off)));
+                       if (i < MBIM_MAX_DNSSRV)
+                               sc->sc_info.ipv4dns[i++] = val;
+                       off += sizeof (uint32_t);
+               }
+               /* XXX add host routes for DNS servers? */
+       }
+
+       if ((avail & MBIM_IPCONF_HAS_MTUINFO)) {
+               val = letoh32(ic->ipv4_mtu);
+               if (ifp->if_hardmtu != val && val <= sc->sc_maxpktlen) {
+                       ifp->if_hardmtu = val;
+                       if (ifp->if_mtu > val)
+                               ifp->if_mtu = val;
+                       log(LOG_INFO, "%s: MTU is %d\n", DEVNAM(sc), val);
+               }
+       }
+
+       avail = letoh32(ic->ipv6_available);
+       if (avail & MBIM_IPCONF_HAS_ADDRINFO) {
+               /* XXX FIXME: IPv6 configuation missing */
+               log(LOG_INFO, "%s: ignoring IPv6 configuration\n", DEVNAM(sc));
+       }
+       if (state != -1)
+               mbim_newstate(sc, state, 0);
+
+done:
+       splx(s);
+       return 1;
+}
+
+/* Code copied from if_spppsubr.c */
+void
+mbim_update_gw(struct ifnet *ifp)
+{
+       unsigned int     tid;
+
+       /* update routing table */
+       for (tid = 0; tid <= RT_TABLEID_MAX; tid++) {
+               while (rtable_walk(tid, AF_INET, mbim_update_gw_walker, ifp) ==
+                   EAGAIN)
+               ;       /* nothing */
+       }
+}
+
+int
+mbim_update_gw_walker(struct rtentry *rt, void *arg, unsigned int id)
+{
+       struct ifnet *ifp = arg;
+
+       if (rt->rt_ifidx == ifp->if_index) {
+               if (rt->rt_ifa->ifa_dstaddr->sa_family !=
+                   rt->rt_gateway->sa_family ||
+                   !ISSET(rt->rt_flags, RTF_GATEWAY))
+                       return 0;       /* do not modify non-gateway routes */
+               log(LOG_INFO, "%s: update gw %s -> %s\n", DEVNAM(ifp->if_softc),
+                   mbim_ntop(rt->rt_gateway),
+                   mbim_ntop(rt->rt_ifa->ifa_dstaddr));
+               rt_setgate(rt, rt->rt_ifa->ifa_dstaddr);
+       }
+       return 0;
+}
+
+void
+mbim_rx(struct mbim_softc *sc)
+{
+       usbd_setup_xfer(sc->sc_rx_xfer, sc->sc_rx_pipe, sc, sc->sc_rx_buf,
+           sc->sc_maxpktlen, USBD_SHORT_XFER_OK | USBD_NO_COPY,
+           USBD_NO_TIMEOUT, mbim_rxeof);
+       usbd_transfer(sc->sc_rx_xfer);
+}
+
+void
+mbim_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+       struct mbim_softc *sc = priv;
+       struct ifnet *ifp = GET_IFP(sc);
+
+       if (usbd_is_dying(sc->sc_udev) || !(ifp->if_flags & IFF_RUNNING))
+               return;
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+                       return;
+               if (usbd_ratecheck(&sc->sc_rx_ratechk))
+                       DPRINTF("%s: rx error: %s\n", DEVNAM(sc),
+                           usbd_errstr(status));
+               if (status == USBD_STALLED)
+                       usbd_clear_endpoint_stall_async(sc->sc_rx_pipe);
+               if (++sc->sc_rx_nerr > 100) {
+                       log(LOG_ERR, "%s: too many rx errors, disabling\n",
+                           DEVNAM(sc));
+                       usbd_deactivate(sc->sc_udev);
+               }
+       } else {
+               sc->sc_rx_nerr = 0;
+               mbim_decap(sc, xfer);
+       }
+
+       mbim_rx(sc);
+       return;
+}
+
+int
+mbim_encap(struct mbim_softc *sc, struct mbuf *m)
+{
+       struct ncm_header16 *hdr;
+       struct ncm_pointer16 *ptr;
+       usbd_status  err;
+       int      len;
+
+       KASSERT(sc->sc_tx_m == NULL);
+
+       hdr = sc->sc_tx_buf;
+       ptr = (struct ncm_pointer16 *)(hdr + 1);
+
+       USETDW(hdr->dwSignature, NCM_HDR16_SIG);
+       USETW(hdr->wHeaderLength, sizeof (*hdr));
+       USETW(hdr->wSequence, sc->sc_tx_seq);
+       sc->sc_tx_seq++;
+       USETW(hdr->wNdpIndex, sizeof (*hdr));
+
+       len = m->m_pkthdr.len;
+       USETDW(ptr->dwSignature, MBIM_NCM_NTH16_SIG(mbim_session_id));
+       USETW(ptr->wLength, sizeof (*ptr));
+       USETW(ptr->wNextNdpIndex, 0);
+       USETW(ptr->dgram[0].wDatagramIndex, MBIM_HDR16_LEN);
+       USETW(ptr->dgram[0].wDatagramLen, len);
+       USETW(ptr->dgram[1].wDatagramIndex, 0);
+       USETW(ptr->dgram[1].wDatagramLen, 0);
+
+       m_copydata(m, 0, len, (caddr_t)(ptr + 1));
+       sc->sc_tx_m = m;
+       len += MBIM_HDR16_LEN;
+       USETW(hdr->wBlockLength, len);
+
+       DPRINTFN(3, "%s: encap %d bytes\n", DEVNAM(sc), len);
+       DDUMPN(5, sc->sc_tx_buf, len);
+       usbd_setup_xfer(sc->sc_tx_xfer, sc->sc_tx_pipe, sc, sc->sc_tx_buf, len,
+           USBD_FORCE_SHORT_XFER | USBD_NO_COPY, mbim_xfer_tout, mbim_txeof);
+       err = usbd_transfer(sc->sc_tx_xfer);
+       if (err != USBD_IN_PROGRESS) {
+               DPRINTF("%s: start tx error: %s\n", DEVNAM(sc),
+                   usbd_errstr(err));
+               return 0;
+       }
+       return 1;
+}
+
+void
+mbim_txeof(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+       struct mbim_softc *sc = priv;
+       struct ifnet *ifp = GET_IFP(sc);
+       int      s;
+
+       s = splnet();
+       ifp->if_flags &= ~IFF_OACTIVE;
+       ifp->if_timer = 0;
+
+       m_freem(sc->sc_tx_m);
+       sc->sc_tx_m = NULL;
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               if (status != USBD_NOT_STARTED && status != USBD_CANCELLED) {
+                       ifp->if_oerrors++;
+                       DPRINTF("%s: tx error: %s\n", DEVNAM(sc),
+                           usbd_errstr(status));
+                       if (status == USBD_STALLED)
+                               usbd_clear_endpoint_stall_async(sc->sc_tx_pipe);
+               }
+       } else {
+               ifp->if_opackets++;
+               if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
+                       mbim_start(ifp);
+       }
+
+       splx(s);
+}
+
+void
+mbim_decap(struct mbim_softc *sc, struct usbd_xfer *xfer)
+{
+       struct ifnet *ifp = GET_IFP(sc);
+       int      s;
+       void    *buf;
+       uint32_t len;
+       char    *dp;
+       struct ncm_header16 *hdr16;
+       struct ncm_header32 *hdr32;
+       struct ncm_pointer16 *ptr16;
+       struct ncm_pointer16_dgram *dgram16;
+       struct ncm_pointer32_dgram *dgram32;
+       uint32_t hsig, psig;
+       int      hlen, blen;
+       int      ptrlen, ptroff, dgentryoff;
+       uint32_t doff, dlen;
+       struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+       struct mbuf *m;
+
+       usbd_get_xfer_status(xfer, NULL, &buf, &len, NULL);
+       DPRINTFN(4, "%s: recv %d bytes\n", DEVNAM(sc), len);
+       DDUMPN(5, buf, len);
+       s = splnet();
+       if (len < sizeof (*hdr16))
+               goto toosmall;
+       if (len > sc->sc_maxpktlen) {
+               DPRINTF("%s: packet too large (%d)\n", DEVNAM(sc), len);
+               goto fail;
+       }
+
+       hdr16 = (struct ncm_header16 *)buf;
+       hsig = UGETDW(hdr16->dwSignature);
+       hlen = UGETW(hdr16->wHeaderLength);
+       switch (hsig) {
+       case NCM_HDR16_SIG:
+               blen = UGETW(hdr16->wBlockLength);
+               if (hlen != sizeof (*hdr16)) {
+                       DPRINTF("%s: bad header len %d for NTH16 (exp %zu)\n",
+                           DEVNAM(sc), hlen, sizeof (*hdr16));
+                       goto fail;
+               }
+               break;
+       case NCM_HDR32_SIG:
+               hdr32 = (struct ncm_header32 *)hdr16;
+               blen = UGETDW(hdr32->dwBlockLength);
+               if (hlen != sizeof (*hdr32)) {
+                       DPRINTF("%s: bad header len %d for NTH32 (exp %zu)\n",
+                           DEVNAM(sc), hlen, sizeof (*hdr32));
+                       goto fail;
+               }
+               break;
+       default:
+               DPRINTF("%s: unsupported NCM header signature (0x%08x)\n",
+                   DEVNAM(sc), hsig);
+               goto fail;
+       }
+       if (len < hlen)
+               goto toosmall;
+       if (len < blen) {
+               DPRINTF("%s: bad NTB len (%d) for %d bytes of data\n",
+                   DEVNAM(sc), blen, len);
+               goto fail;
+       }
+
+       ptroff = hlen;
+       ptr16 = (struct ncm_pointer16 *)(buf + ptroff);
+       psig = UGETDW(ptr16->dwSignature);
+       ptrlen = UGETW(ptr16->wLength);
+       if (len < ptrlen + ptroff)
+               goto toosmall;
+       if (!MBIM_NCM_NTH16_ISISG(psig) && !MBIM_NCM_NTH32_ISISG(psig)) {
+               DPRINTF("%s: unsupported NCM pointer signature (0x%08x)\n",
+                   DEVNAM(sc), psig);
+               goto fail;
+       }
+
+       switch (hsig) {
+       case NCM_HDR16_SIG:
+               dgentryoff = offsetof(struct ncm_pointer16, dgram);
+               break;
+       case NCM_HDR32_SIG:
+               dgentryoff = offsetof(struct ncm_pointer32, dgram);
+               break;
+       default:
+               goto fail;
+       }
+
+       while (dgentryoff < ptrlen) {
+               switch (hsig) {
+               case NCM_HDR16_SIG:
+                       if (ptroff + dgentryoff < sizeof (*dgram16))
+                               goto done;
+                       dgram16 = (struct ncm_pointer16_dgram *)
+                           (buf + ptroff + dgentryoff);
+                       dgentryoff += sizeof (*dgram16);
+                       dlen = UGETW(dgram16->wDatagramLen);
+                       doff = UGETW(dgram16->wDatagramIndex);
+                       break;
+               case NCM_HDR32_SIG:
+                       if (ptroff + dgentryoff < sizeof (*dgram32))
+                               goto done;
+                       dgram32 = (struct ncm_pointer32_dgram *)
+                           (buf + ptroff + dgentryoff);
+                       dgentryoff += sizeof (*dgram32);
+                       dlen = UGETDW(dgram32->dwDatagramLen);
+                       doff = UGETDW(dgram32->dwDatagramIndex);
+                       break;
+               default:
+                       ifp->if_ierrors++;
+                       goto done;
+               }
+
+               /* Terminating zero entry */
+               if (dlen == 0 && doff == 0)
+                       break;
+               if (len < dlen + doff) {
+                       /* Skip giant datagram but continue processing */
+                       DPRINTF("%s: datagram too large (%d @ off %d)\n",
+                           DEVNAM(sc), dlen, doff);
+                       continue;
+               }
+
+               dp = buf + doff;
+               DPRINTFN(3, "%s: decap %d bytes\n", DEVNAM(sc), dlen);
+               m = m_devget(dp, dlen, 0);
+               if (m == NULL) {
+                       ifp->if_iqdrops++;
+                       continue;
+               }
+
+               ml_enqueue(&ml, m);
+       }
+done:
+       if_input(ifp, &ml);
+       splx(s);
+       return;
+toosmall:
+       DPRINTF("%s: packet too small (%d)\n", DEVNAM(sc), len);
+fail:
+       ifp->if_ierrors++;
+       splx(s);
+}
+
+usbd_status
+mbim_send_encap_command(struct mbim_softc *sc, void *data, int len)
+{
+       struct usbd_xfer *xfer;
+       usb_device_request_t req;
+       char *buf;
+
+       if (len > sc->sc_ctrl_len)
+               return USBD_INVAL;
+
+       if ((xfer = usbd_alloc_xfer(sc->sc_udev)) == NULL)
+               return USBD_NOMEM;
+       if ((buf = usbd_alloc_buffer(xfer, len)) == NULL) {
+               usbd_free_xfer(xfer);
+               return USBD_NOMEM;
+       }
+       memcpy(buf, data, len);
+
+       /* XXX FIXME: if (total len > sc->sc_ctrl_len) => must fragment */
+       req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
+       req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND;
+       USETW(req.wValue, 0);
+       USETW(req.wIndex, sc->sc_ctrl_ifaceno);
+       USETW(req.wLength, len);
+       DELAY(mbim_delay);
+       return usbd_request_async(xfer, &req, NULL, NULL);
+}
+
+int
+mbim_get_encap_response(struct mbim_softc *sc, void *buf, int *len)
+{
+       usb_device_request_t req;
+       usbd_status err;
+
+       req.bmRequestType = UT_READ_CLASS_INTERFACE;
+       req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE;
+       USETW(req.wValue, 0);
+       USETW(req.wIndex, sc->sc_ctrl_ifaceno);
+       USETW(req.wLength, *len);
+       /* XXX FIXME: re-assemble fragments */
+
+       DELAY(mbim_delay);
+       err = usbd_do_request_flags(sc->sc_udev, &req, buf, USBD_SHORT_XFER_OK,
+           len, mbim_xfer_tout);
+       if (err == USBD_NORMAL_COMPLETION)
+               return 1;
+       DPRINTF("%s: ctrl recv: %s\n", DEVNAM(sc), usbd_errstr(err));
+       return 0;
+}
+
+void
+mbim_ctrl_msg(struct mbim_softc *sc, uint32_t req, void *data, int len)
+{
+       uint32_t tid;
+       struct mbim_msghdr *hdr = data;
+       usbd_status err;
+       int      s;
+
+       assertwaitok();
+       if (usbd_is_dying(sc->sc_udev))
+               return;
+       if (len < sizeof (*hdr))
+               return;
+       tid = ++sc->sc_tid;
+
+       hdr->type = htole32(req);
+       hdr->len = htole32(len);
+       hdr->tid = htole32(tid);
+
+#ifdef MBIM_DEBUG
+       if (mbim_debug) {
+               const char *op, *str;
+               if (req == MBIM_COMMAND_MSG) {
+                       struct mbim_h2f_cmd *c = data;
+                       if (letoh32(c->op) == MBIM_CMDOP_SET)
+                               op = "set";
+                       else
+                               op = "qry";
+                       str = mbim_cid2str(letoh32(c->cid));
+               } else {
+                       op = "snd";
+                       str = mbim_request2str(req);
+               }
+               DPRINTF("%s: -> %s %s (tid %u)\n", DEVNAM(sc), op, str, tid);
+       }
+#endif
+       s = splusb();
+       err = mbim_send_encap_command(sc, data, len);
+       splx(s);
+       if (err != USBD_NORMAL_COMPLETION) {
+               log(LOG_ERR, "%s: send %s msg (tid %u) failed: %s\n",
+                   DEVNAM(sc), mbim_request2str(req), tid, usbd_errstr(err));
+
+               /* will affect other transactions, too */
+               usbd_abort_pipe(sc->sc_udev->default_pipe);
+       } else {
+               DPRINTFN(2, "%s: sent %s (tid %u)\n", DEVNAM(sc),
+                   mbim_request2str(req), tid);
+               DDUMPN(3, data, len);
+       }
+       return;
+}
+
+void
+mbim_open(struct mbim_softc *sc)
+{
+       struct mbim_h2f_openmsg msg;
+
+       memset(&msg, 0, sizeof (msg));
+       msg.maxlen = htole32(sc->sc_ctrl_len);
+       mbim_ctrl_msg(sc, MBIM_OPEN_MSG, &msg, sizeof (msg));
+       return;
+}
+
+void
+mbim_close(struct mbim_softc *sc)
+{
+       struct mbim_h2f_closemsg msg;
+
+       memset(&msg, 0, sizeof (msg));
+       mbim_ctrl_msg(sc, MBIM_CLOSE_MSG, &msg, sizeof (msg));
+}
+
+int
+mbim_setpin(struct mbim_softc *sc, int op, int is_puk, void *pin, int pinlen,
+    void *newpin, int newpinlen)
+{
+       struct mbim_cid_pin cp;
+       int      off;
+
+       if (pinlen == 0)
+               return 0;
+       if (pinlen < 0 || pinlen > MBIM_PIN_MAXLEN ||
+           newpinlen < 0 || newpinlen > MBIM_PIN_MAXLEN ||
+           op < 0 || op > MBIM_PIN_OP_CHANGE ||
+           (is_puk && op != MBIM_PIN_OP_ENTER))
+               return EINVAL;
+
+       memset(&cp, 0, sizeof (cp));
+       cp.type = htole32(is_puk ? MBIM_PIN_TYPE_PUK1 : MBIM_PIN_TYPE_PIN1);
+
+       off = offsetof(struct mbim_cid_pin, data);
+       if (!mbim_addstr(&cp, sizeof (cp), &off, pin, pinlen,
+           &cp.pin_offs, &cp.pin_size))
+               return EINVAL;
+
+       cp.op  = htole32(op);
+       if (newpinlen) {
+               if (!mbim_addstr(&cp, sizeof (cp), &off, newpin, newpinlen,
+                   &cp.newpin_offs, &cp.newpin_size))
+                       return EINVAL;
+       } else {
+               if ((op == MBIM_PIN_OP_CHANGE) || is_puk)
+                       return EINVAL;
+               if (!mbim_addstr(&cp, sizeof (cp), &off, NULL, 0,
+                   &cp.newpin_offs, &cp.newpin_size))
+                       return EINVAL;
+       }
+       mbim_cmd(sc, MBIM_CID_PIN, MBIM_CMDOP_SET, &cp, off);
+       return 0;
+}
+
+void
+mbim_setdataclass(struct mbim_softc *sc)
+{
+       struct mbim_cid_registration_state rs;
+       uint32_t         classes;
+
+       if (sc->sc_info.supportedclasses == MBIM_DATACLASS_NONE)
+               return;
+
+       memset(&rs, 0, sizeof (rs));
+       rs.regaction = htole32(MBIM_REGACTION_AUTOMATIC);
+       classes = sc->sc_info.supportedclasses;
+       if (sc->sc_info.preferredclasses != MBIM_DATACLASS_NONE)
+               classes &= sc->sc_info.preferredclasses;
+       rs.data_class = htole32(classes);
+       mbim_cmd(sc, MBIM_CID_REGISTER_STATE, MBIM_CMDOP_SET, &rs, sizeof (rs));
+}
+
+void
+mbim_radio(struct mbim_softc *sc, int on)
+{
+       struct mbim_cid_radio_state s;
+
+       DPRINTF("%s: set radio %s\n", DEVNAM(sc), on ? "on" : "off");
+       memset(&s, 0, sizeof (s));
+       s.state = htole32(on ? MBIM_RADIO_STATE_ON : MBIM_RADIO_STATE_OFF);
+       mbim_cmd(sc, MBIM_CID_RADIO_STATE, MBIM_CMDOP_SET, &s, sizeof (s));
+}
+
+void
+mbim_packet_service(struct mbim_softc *sc, int attach)
+{
+       struct mbim_cid_packet_service  s;
+
+       DPRINTF("%s: %s packet service\n", DEVNAM(sc),
+           attach ? "attach" : "detach");
+       memset(&s, 0, sizeof (s));
+       s.action = htole32(attach ?
+           MBIM_PKTSERVICE_ACTION_ATTACH : MBIM_PKTSERVICE_ACTION_DETACH);
+       mbim_cmd(sc, MBIM_CID_PACKET_SERVICE, MBIM_CMDOP_SET, &s, sizeof (s));
+}
+
+void
+mbim_connect(struct mbim_softc *sc)
+{
+       if (sc->sc_info.regstate == MBIM_REGSTATE_ROAMING && !sc->sc_roaming) {
+               log(LOG_INFO, "%s: connection disabled in roaming network\n",
+                   DEVNAM(sc));
+               return;
+       }
+       log(LOG_DEBUG, "%s: connecting ...\n", DEVNAM(sc));
+       mbim_send_connect(sc, MBIM_CONNECT_ACTIVATE);
+}
+
+void
+mbim_disconnect(struct mbim_softc *sc)
+{
+       log(LOG_DEBUG, "%s: disconnecting ...\n", DEVNAM(sc));
+       mbim_send_connect(sc, MBIM_CONNECT_DEACTIVATE);
+}
+
+void
+mbim_send_connect(struct mbim_softc *sc, int command)
+{
+       struct mbim_cid_connect *c;
+       int      off;
+
+       /* Too large or the stack */
+       c = malloc(sizeof (*c), M_USBDEV, M_WAIT|M_ZERO);
+       c->sessionid = htole32(mbim_session_id);
+       c->command = htole32(command);
+       off = offsetof(struct mbim_cid_connect, data);
+       if (!mbim_addstr(c, sizeof (*c), &off, sc->sc_info.apn,
+           sc->sc_info.apnlen, &c->access_offs, &c->access_size))
+               goto done;
+       /* XXX FIXME: support user name and passphrase */
+       c->user_offs = htole32(0);
+       c->user_size = htole32(0);
+       c->passwd_offs = htole32(0);
+       c->passwd_size = htole32(0);
+       c->authprot = htole32(MBIM_AUTHPROT_NONE);
+       c->compression = htole32(MBIM_COMPRESSION_NONE);
+       c->iptype = htole32(MBIM_CONTEXT_IPTYPE_IPV4);
+       memcpy(c->context, mbim_uuid_context_internet, sizeof (c->context));
+       mbim_cmd(sc, MBIM_CID_CONNECT, MBIM_CMDOP_SET, c, off);
+done:
+       free(c, M_USBDEV, sizeof (*c));
+       return;
+}
+
+void
+mbim_qry_ipconfig(struct mbim_softc *sc)
+{
+       struct mbim_cid_ip_configuration_info ipc;
+
+       memset(&ipc, 0, sizeof (ipc));
+       ipc.sessionid = htole32(mbim_session_id);
+       mbim_cmd(sc, MBIM_CID_IP_CONFIGURATION, MBIM_CMDOP_QRY,
+           &ipc, sizeof (ipc));
+}
+
+void
+mbim_cmd(struct mbim_softc *sc, int cid, int op, void *data, int len)
+{
+       struct mbim_h2f_cmd *cmd;
+       int     totlen;
+
+       /* XXX FIXME support sending fragments */
+       if (sizeof (*cmd) + len > sc->sc_ctrl_len) {
+               DPRINTF("%s: set %s msg too long: cannot send\n",
+                   DEVNAM(sc), mbim_cid2str(cid));
+               return;
+       }
+       cmd = sc->sc_ctrl_msg;
+       memset(cmd, 0, sizeof (*cmd));
+       cmd->frag.nfrag = htole32(1);
+       memcpy(cmd->devid, mbim_uuid_basic_connect, sizeof (cmd->devid));
+       cmd->cid = htole32(cid);
+       cmd->op = htole32(op);
+       cmd->infolen = htole32(len);
+       totlen = sizeof (*cmd);
+       if (len > 0) {
+               memcpy(cmd + 1, data, len);
+               totlen += len;
+       }
+       mbim_ctrl_msg(sc, MBIM_COMMAND_MSG, cmd, totlen);
+}
+
+void
+mbim_command_done(struct mbim_softc *sc, void *data, int len)
+{
+       struct mbim_f2h_cmddone *cmd = data;
+       uint32_t status;
+       uint32_t cid;
+       uint32_t infolen;
+
+       if (len < sizeof (*cmd)) {
+               DPRINTF("%s: discard short %s messsage\n", DEVNAM(sc),
+                   mbim_request2str(letoh32(cmd->hdr.type)));
+               return;
+       }
+       cid = letoh32(cmd->cid);
+       if (memcmp(cmd->devid, mbim_uuid_basic_connect, sizeof (cmd->devid))) {
+               DPRINTF("%s: discard %s messsage for other UUID '%s'\n",
+                   DEVNAM(sc), mbim_request2str(letoh32(cmd->hdr.type)),
+                   mbim_uuid2str(cmd->devid));
+               return;
+       }
+
+       status = letoh32(cmd->status);
+       switch (status) {
+       case MBIM_STATUS_SUCCESS:
+               break;
+       case MBIM_STATUS_NOT_INITIALIZED:
+               log(LOG_ERR, "%s: SIM not initialized (PIN missing)\n",
+                   DEVNAM(sc));
+               return;
+       case MBIM_STATUS_PIN_REQUIRED:
+               sc->sc_info.pin_state = MBIM_PIN_REQUIRED;
+               /*FALLTHROUGH*/
+       default:
+               log(LOG_ERR, "%s: set/qry %s failed: %s\n", DEVNAM(sc),
+                   mbim_cid2str(cid), mbim_status2str(status));
+               return;
+       }
+
+       infolen = letoh32(cmd->infolen);
+       if (len < sizeof (*cmd) + infolen) {
+               DPRINTF("%s: discard truncated %s messsage (want %d, got %d)\n",
+                   DEVNAM(sc), mbim_cid2str(cid),
+                   (int)sizeof (*cmd) + infolen, len);
+               return;
+       }
+       DPRINTFN(2, "%s: set/qry %s done\n", DEVNAM(sc), mbim_cid2str(cid));
+       mbim_decode_cid(sc, cid, cmd->info, infolen);
+}
+
+void
+mbim_decode_cid(struct mbim_softc *sc, uint32_t cid, void *data, int len)
+{
+       int      ok = 1;
+
+       switch (cid) {
+       case MBIM_CID_DEVICE_CAPS:
+               ok = mbim_decode_devices_caps(sc, data, len);
+               break;
+       case MBIM_CID_SUBSCRIBER_READY_STATUS:
+               ok = mbim_decode_subscriber_status(sc, data, len);
+               break;
+       case MBIM_CID_RADIO_STATE:
+               ok = mbim_decode_radio_state(sc, data, len);
+               break;
+       case MBIM_CID_PIN:
+               ok = mbim_decode_pin(sc, data, len);
+               break;
+       case MBIM_CID_REGISTER_STATE:
+               ok = mbim_decode_register_state(sc, data, len);
+               break;
+       case MBIM_CID_PACKET_SERVICE:
+               ok = mbim_decode_packet_service(sc, data, len);
+               break;
+       case MBIM_CID_SIGNAL_STATE:
+               ok = mbim_decode_signal_state(sc, data, len);
+               break;
+       case MBIM_CID_CONNECT:
+               ok = mbim_decode_connect_info(sc, data, len);
+               break;
+       case MBIM_CID_IP_CONFIGURATION:
+               ok = mbim_decode_ip_configuration(sc, data, len);
+               break;
+       default:
+               /*
+                * Note: the above list is incomplete and only contains
+                *      mandatory CIDs from the BASIC_CONNECT set.
+                *      So alternate values are not unusual.
+                */
+               DPRINTFN(4, "%s: ignore %s\n", DEVNAM(sc), mbim_cid2str(cid));
+               break;
+       }
+       if (!ok)
+               DPRINTF("%s: discard %s with bad info length %d\n",
+                   DEVNAM(sc), mbim_cid2str(cid), len);
+       return;
+}
+
+void
+mbim_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
+{
+       struct mbim_softc *sc = priv;
+       int      total_len;
+
+       if (status != USBD_NORMAL_COMPLETION) {
+               DPRINTF("%s: notification error: %s\n", DEVNAM(sc),
+                   usbd_errstr(status));
+               if (status == USBD_STALLED)
+                       usbd_clear_endpoint_stall_async(sc->sc_ctrl_pipe);
+               return;
+       }
+       usbd_get_xfer_status(xfer, NULL, NULL, &total_len, NULL);
+       if (total_len < UCDC_NOTIFICATION_LENGTH) {
+               DPRINTF("%s: short notification (%d<%d)\n", DEVNAM(sc),
+                   total_len, UCDC_NOTIFICATION_LENGTH);
+                   return;
+       }
+       if (sc->sc_intr_msg.bmRequestType != UCDC_NOTIFICATION) {
+               DPRINTF("%s: unexpected notification (type=0x%02x)\n",
+                   DEVNAM(sc), sc->sc_intr_msg.bmRequestType);
+               return;
+       }
+
+       switch (sc->sc_intr_msg.bNotification) {
+       case UCDC_N_NETWORK_CONNECTION:
+               log(LOG_DEBUG, "%s: network %sconnected\n", DEVNAM(sc),
+                   UGETW(sc->sc_intr_msg.wValue) ? "" : "dis");
+               break;
+       case UCDC_N_RESPONSE_AVAILABLE:
+               DPRINTFN(2, "%s: mbim_intr: response available\n", DEVNAM(sc));
+               ++sc->sc_nresp;
+               usb_add_task(sc->sc_udev, &sc->sc_get_response_task);
+               break;
+       case UCDC_N_CONNECTION_SPEED_CHANGE:
+               DPRINTFN(2, "%s: mbim_intr: connection speed changed\n",
+                   DEVNAM(sc));
+               break;
+       default:
+               DPRINTF("%s: unexpected notifiation (0x%02x)\n",
+                   DEVNAM(sc), sc->sc_intr_msg.bNotification);
+               break;
+       }
+}
+
+/*
+ * Diagnostic routines
+ */
+char *
+mbim_ntop(struct sockaddr *sa)
+{
+#define NUMBUFS                4
+       static char astr[NUMBUFS][INET_ADDRSTRLEN];
+       static unsigned nbuf = 0;
+       char    *s;
+       void    *d;
+
+       s = astr[nbuf++];
+       if (nbuf >= NUMBUFS)
+               nbuf = 0;
+
+       if (sa->sa_family == AF_INET)
+               d = &satosin(sa)->sin_addr;
+       else
+               d = &satosin6(sa)->sin6_addr;
+
+       inet_ntop(sa->sa_family, d, s, sizeof (astr[0]));
+       return s;
+}
+
+#ifdef MBIM_DEBUG
+char *
+mbim_uuid2str(uint8_t uuid[MBIM_UUID_LEN])
+{
+       static char uuidstr[2 * MBIM_UUID_LEN + 5];
+
+#define UUID_BFMT      "%02X"
+#define UUID_SEP       "-"
+       snprintf(uuidstr, sizeof (uuidstr),
+           UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT UUID_SEP
+           UUID_BFMT UUID_BFMT UUID_SEP
+           UUID_BFMT UUID_BFMT UUID_SEP
+           UUID_BFMT UUID_BFMT UUID_SEP
+           UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT UUID_BFMT,
+           uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
+           uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
+           uuid[12], uuid[13], uuid[14], uuid[15]);
+       return uuidstr;
+}
+
+void
+mbim_dump(void *buf, int len)
+{
+       int      i = 0;
+       uint8_t *c = buf;
+
+       if (len == 0)
+               return;
+       while (i < len) {
+               if ((i % 16) == 0) {
+                       if (i > 0)
+                               addlog("\n");
+                       log(LOG_DEBUG, "%4d:  ", i);
+               }
+               addlog(" %02x", *c);
+               c++;
+               i++;
+       }
+       addlog("\n");
+}
+#endif /* MBIM_DEBUG */
Index: sys/dev/usb/if_mbim.h
===================================================================
RCS file: sys/dev/usb/if_mbim.h
diff -N sys/dev/usb/if_mbim.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/dev/usb/if_mbim.h       23 May 2016 09:50:08 -0000
@@ -0,0 +1,376 @@
+/*     $OpenBSD$ */
+
+/*
+ * Copyright (c) 2016 genua mbH
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Mobile Broadband Interface Model
+ * http://www.usb.org/developers/docs/devclass_docs/MBIM-Compliance-1.0.pdf
+ */
+
+struct mbim_valdescr {
+       int      val;
+       char    *descr;
+};
+
+static const char *
+mbim_val2descr(const struct mbim_valdescr *vdp, int val)
+{
+       static char sval[32];
+
+       while (vdp->descr != NULL) {
+               if (vdp->val == val)
+                       return vdp->descr;
+               vdp++;
+       }
+       snprintf(sval, sizeof (sval), "#%d", val);
+       return sval;
+}
+
+#define MBIM_REGSTATE_DESCRIPTIONS {                           \
+       { MBIM_REGSTATE_UNKNOWN,        "unknown" },            \
+       { MBIM_REGSTATE_DEREGISTERED,   "not registered" },     \
+       { MBIM_REGSTATE_SEARCHING,      "searching" },          \
+       { MBIM_REGSTATE_HOME,           "home network" },       \
+       { MBIM_REGSTATE_ROAMING,        "roaming network" },    \
+       { MBIM_REGSTATE_PARTNER,        "partner network" },    \
+       { MBIM_REGSTATE_DENIED,         "access denied" },      \
+       { 0, NULL } }
+
+#define MBIM_DATACLASS_DESCRIPTIONS {                                  \
+       { MBIM_DATACLASS_NONE,                          "none" },       \
+       { MBIM_DATACLASS_GPRS,                          "GPRS" },       \
+       { MBIM_DATACLASS_EDGE,                          "EDGE" },       \
+       { MBIM_DATACLASS_UMTS,                          "UMTS" },       \
+       { MBIM_DATACLASS_HSDPA,                         "HSDPA" },      \
+       { MBIM_DATACLASS_HSUPA,                         "HSUPA" },      \
+       { MBIM_DATACLASS_HSDPA | MBIM_DATACLASS_HSUPA,  "HSPA" },       \
+       { MBIM_DATACLASS_LTE,                           "LTE" },        \
+       { MBIM_DATACLASS_1XRTT,                         "CDMA2000" },   \
+       { MBIM_DATACLASS_1XEVDO,                        "CDMA2000" },   \
+       { MBIM_DATACLASS_1XEVDO_REV_A,                  "CDMA2000" },   \
+       { MBIM_DATACLASS_1XEVDV,                        "CDMA2000" },   \
+       { MBIM_DATACLASS_3XRTT,                         "CDMA2000" },   \
+       { MBIM_DATACLASS_1XEVDO_REV_B,                  "CDMA2000" },   \
+       { MBIM_DATACLASS_UMB,                           "CDMA2000" },   \
+       { MBIM_DATACLASS_CUSTOM,                        "custom" },     \
+       { 0, NULL } }
+
+#define MBIM_1TO1_DESCRIPTION(m)       { (m), #m }
+#define MBIM_MESSAGES_DESCRIPTIONS {                           \
+       MBIM_1TO1_DESCRIPTION(MBIM_OPEN_MSG),                   \
+       MBIM_1TO1_DESCRIPTION(MBIM_CLOSE_MSG),                  \
+       MBIM_1TO1_DESCRIPTION(MBIM_COMMAND_MSG),                \
+       MBIM_1TO1_DESCRIPTION(MBIM_HOST_ERROR_MSG),             \
+       MBIM_1TO1_DESCRIPTION(MBIM_OPEN_DONE),                  \
+       MBIM_1TO1_DESCRIPTION(MBIM_CLOSE_DONE),                 \
+       MBIM_1TO1_DESCRIPTION(MBIM_COMMAND_DONE),               \
+       MBIM_1TO1_DESCRIPTION(MBIM_FUNCTION_ERROR_MSG),         \
+       MBIM_1TO1_DESCRIPTION(MBIM_INDICATE_STATUS_MSG),        \
+       { 0, NULL } }
+
+#define MBIM_STATUS_DESCRIPTION(m)     { MBIM_STATUS_ ## m, #m }
+#define MBIM_STATUS_DESCRIPTIONS {                                     \
+       MBIM_STATUS_DESCRIPTION(SUCCESS),                               \
+       MBIM_STATUS_DESCRIPTION(BUSY),                                  \
+       MBIM_STATUS_DESCRIPTION(FAILURE),                               \
+       MBIM_STATUS_DESCRIPTION(SIM_NOT_INSERTED),                      \
+       MBIM_STATUS_DESCRIPTION(BAD_SIM),                               \
+       MBIM_STATUS_DESCRIPTION(PIN_REQUIRED),                          \
+       MBIM_STATUS_DESCRIPTION(PIN_DISABLED),                          \
+       MBIM_STATUS_DESCRIPTION(NOT_REGISTERED),                        \
+       MBIM_STATUS_DESCRIPTION(PROVIDERS_NOT_FOUND),                   \
+       MBIM_STATUS_DESCRIPTION(NO_DEVICE_SUPPORT),                     \
+       MBIM_STATUS_DESCRIPTION(PROVIDER_NOT_VISIBLE),                  \
+       MBIM_STATUS_DESCRIPTION(DATA_CLASS_NOT_AVAILABLE),              \
+       MBIM_STATUS_DESCRIPTION(PACKET_SERVICE_DETACHED),               \
+       MBIM_STATUS_DESCRIPTION(MAX_ACTIVATED_CONTEXTS),                \
+       MBIM_STATUS_DESCRIPTION(NOT_INITIALIZED),                       \
+       MBIM_STATUS_DESCRIPTION(VOICE_CALL_IN_PROGRESS),                \
+       MBIM_STATUS_DESCRIPTION(CONTEXT_NOT_ACTIVATED),                 \
+       MBIM_STATUS_DESCRIPTION(SERVICE_NOT_ACTIVATED),                 \
+       MBIM_STATUS_DESCRIPTION(INVALID_ACCESS_STRING),                 \
+       MBIM_STATUS_DESCRIPTION(INVALID_USER_NAME_PWD),                 \
+       MBIM_STATUS_DESCRIPTION(RADIO_POWER_OFF),                       \
+       MBIM_STATUS_DESCRIPTION(INVALID_PARAMETERS),                    \
+       MBIM_STATUS_DESCRIPTION(READ_FAILURE),                          \
+       MBIM_STATUS_DESCRIPTION(WRITE_FAILURE),                         \
+       MBIM_STATUS_DESCRIPTION(NO_PHONEBOOK),                          \
+       MBIM_STATUS_DESCRIPTION(PARAMETER_TOO_LONG),                    \
+       MBIM_STATUS_DESCRIPTION(STK_BUSY),                              \
+       MBIM_STATUS_DESCRIPTION(OPERATION_NOT_ALLOWED),                 \
+       MBIM_STATUS_DESCRIPTION(MEMORY_FAILURE),                        \
+       MBIM_STATUS_DESCRIPTION(INVALID_MEMORY_INDEX),                  \
+       MBIM_STATUS_DESCRIPTION(MEMORY_FULL),                           \
+       MBIM_STATUS_DESCRIPTION(FILTER_NOT_SUPPORTED),                  \
+       MBIM_STATUS_DESCRIPTION(DSS_INSTANCE_LIMIT),                    \
+       MBIM_STATUS_DESCRIPTION(INVALID_DEVICE_SERVICE_OPERATION),      \
+       MBIM_STATUS_DESCRIPTION(AUTH_INCORRECT_AUTN),                   \
+       MBIM_STATUS_DESCRIPTION(AUTH_SYNC_FAILURE),                     \
+       MBIM_STATUS_DESCRIPTION(AUTH_AMF_NOT_SET),                      \
+       MBIM_STATUS_DESCRIPTION(CONTEXT_NOT_SUPPORTED),                 \
+       MBIM_STATUS_DESCRIPTION(SMS_UNKNOWN_SMSC_ADDRESS),              \
+       MBIM_STATUS_DESCRIPTION(SMS_NETWORK_TIMEOUT),                   \
+       MBIM_STATUS_DESCRIPTION(SMS_LANG_NOT_SUPPORTED),                \
+       MBIM_STATUS_DESCRIPTION(SMS_ENCODING_NOT_SUPPORTED),            \
+       MBIM_STATUS_DESCRIPTION(SMS_FORMAT_NOT_SUPPORTED),              \
+       { 0, NULL } }
+
+#define MBIM_ERROR_DESCRIPTION(m)      { MBIM_ERROR_ ## m, #m }
+#define MBIM_ERROR_DESCRIPTIONS {                                      \
+       MBIM_ERROR_DESCRIPTION(TIMEOUT_FRAGMENT),                       \
+       MBIM_ERROR_DESCRIPTION(FRAGMENT_OUT_OF_SEQUENCE),               \
+       MBIM_ERROR_DESCRIPTION(LENGTH_MISMATCH),                        \
+       MBIM_ERROR_DESCRIPTION(DUPLICATED_TID),                         \
+       MBIM_ERROR_DESCRIPTION(NOT_OPENED),                             \
+       MBIM_ERROR_DESCRIPTION(UNKNOWN),                                \
+       MBIM_ERROR_DESCRIPTION(CANCEL),                                 \
+       MBIM_ERROR_DESCRIPTION(MAX_TRANSFER),                           \
+       { 0, NULL } }
+
+#define MBIM_CID_DESCRIPTIONS {                                                
\
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_DEVICE_CAPS),                    \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_SUBSCRIBER_READY_STATUS),        \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_RADIO_STATE),                    \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_PIN),                            \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_PIN_LIST),                       \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_HOME_PROVIDER),                  \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_PREFERRED_PROVIDERS),            \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_VISIBLE_PROVIDERS),              \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_REGISTER_STATE),                 \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_PACKET_SERVICE),                 \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_SIGNAL_STATE),                   \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_CONNECT),                        \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_PROVISIONED_CONTEXTS),           \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_SERVICE_ACTIVATION),             \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_IP_CONFIGURATION),               \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_DEVICE_SERVICES),                \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST),  \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_PACKET_STATISTICS),              \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_NETWORK_IDLE_HINT),              \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_EMERGENCY_MODE),                 \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_IP_PACKET_FILTERS),              \
+       MBIM_1TO1_DESCRIPTION(MBIM_CID_MULTICARRIER_PROVIDERS),         \
+       { 0, NULL } }
+
+#define MBIM_SIMSTATE_DESCRIPTIONS {                                   \
+       { MBIM_SIMSTATE_NOTINITIALIZED, "not initialized" },            \
+       { MBIM_SIMSTATE_INITIALIZED, "initialized" },                   \
+       { MBIM_SIMSTATE_NOTINSERTED, "not inserted" },                  \
+       { MBIM_SIMSTATE_BADSIM, "bad type" },                           \
+       { MBIM_SIMSTATE_FAILURE, "failed" },                            \
+       { MBIM_SIMSTATE_NOTACTIVATED, "not activated" },                \
+       { MBIM_SIMSTATE_LOCKED, "locked" },                             \
+       { 0, NULL } }
+
+#define MBIM_PINTYPE_DESCRIPTIONS {                                    \
+       { MBIM_PIN_TYPE_NONE, "none" },                                 \
+       { MBIM_PIN_TYPE_CUSTOM, "custom" },                             \
+       { MBIM_PIN_TYPE_PIN1, "PIN1" },                                 \
+       { MBIM_PIN_TYPE_PIN2, "PIN2" },                                 \
+       { MBIM_PIN_TYPE_DEV_SIM_PIN, "device PIN" },                    \
+       { MBIM_PIN_TYPE_DEV_FIRST_SIM_PIN, "device 1st PIN" },          \
+       { MBIM_PIN_TYPE_NETWORK_PIN, "network PIN" },                   \
+       { MBIM_PIN_TYPE_NETWORK_SUBSET_PIN, "network subset PIN" },     \
+       { MBIM_PIN_TYPE_SERVICE_PROVIDER_PIN, "provider PIN" },         \
+       { MBIM_PIN_TYPE_CORPORATE_PIN, "corporate PIN" },               \
+       { MBIM_PIN_TYPE_SUBSIDY_LOCK, "subsidy lock" },                 \
+       { MBIM_PIN_TYPE_PUK1, "PUK" },                                  \
+       { MBIM_PIN_TYPE_PUK2, "PUK2" },                                 \
+       { MBIM_PIN_TYPE_DEV_FIRST_SIM_PUK, "device 1st PUK" },          \
+       { MBIM_PIN_TYPE_NETWORK_PUK, "network PUK" },                   \
+       { MBIM_PIN_TYPE_NETWORK_SUBSET_PUK, "network subset PUK" },     \
+       { MBIM_PIN_TYPE_SERVICE_PROVIDER_PUK, "provider PUK" },         \
+       { MBIM_PIN_TYPE_CORPORATE_PUK, "corporate PUK" },               \
+       { 0, NULL } }
+
+#define MBIM_PKTSRV_STATE_DESCRIPTIONS {                               \
+       { MBIM_PKTSERVICE_STATE_UNKNOWN, "unknown" },                   \
+       { MBIM_PKTSERVICE_STATE_ATTACHING, "attaching" },               \
+       { MBIM_PKTSERVICE_STATE_ATTACHED, "attached" },                 \
+       { MBIM_PKTSERVICE_STATE_DETACHING, "detaching" },               \
+       { MBIM_PKTSERVICE_STATE_DETACHED, "detached" },                 \
+       { 0, NULL } }
+
+#define MBIM_ACTIVATION_STATE_DESCRIPTIONS {                           \
+       { MBIM_ACTIVATION_STATE_UNKNOWN, "unknown" },                   \
+       { MBIM_ACTIVATION_STATE_ACTIVATED, "activated" },               \
+       { MBIM_ACTIVATION_STATE_ACTIVATING, "activating" },             \
+       { MBIM_ACTIVATION_STATE_DEACTIVATED, "deactivated" },           \
+       { MBIM_ACTIVATION_STATE_DEACTIVATING, "deactivating" },         \
+       { 0, NULL } }
+
+/*
+ * Driver internal state
+ */
+enum mbim_state {
+       MBIM_S_DOWN = 0,        /* interface down */
+       MBIM_S_OPEN,            /* MBIM device has been opened */
+       MBIM_S_RADIO,           /* radio is on */
+       MBIM_S_SIMREADY,        /* SIM is ready */
+       MBIM_S_ATTACHED,        /* packet service is attached */
+       MBIM_S_CONNECTED,       /* connected to provider */
+       MBIM_S_UP,              /* have IP configuration */
+};
+
+#define MBIM_INTERNAL_STATE_DESCRIPTIONS {     \
+       { MBIM_S_DOWN, "down" },                \
+       { MBIM_S_OPEN, "open" },                \
+       { MBIM_S_RADIO, "radio on" },           \
+       { MBIM_S_SIMREADY, "SIM is ready" },            \
+       { MBIM_S_ATTACHED, "attached" },        \
+       { MBIM_S_CONNECTED, "connected" },      \
+       { MBIM_S_UP, "up" },                    \
+       { 0, NULL } }
+
+/*
+ * MBIM parameters (SIOC[GS]MBIMPARAM ioctls)
+ */
+struct mbim_parameter {
+       int                     op;
+       int                     is_puk;
+       char                    pin[MBIM_PIN_MAXLEN];
+       int                     pinlen;
+
+       char                    newpin[MBIM_PIN_MAXLEN];
+       int                     newpinlen;
+
+#define MBIM_APN_MAXLEN                        100
+       uint16_t                apn[MBIM_APN_MAXLEN];
+       int                     apnlen;
+
+       int                     roaming;
+       uint32_t                preferredclasses;
+};
+
+/*
+ * MBIM device status info (SIOCGMBIMINFO ioctl)
+ */
+struct mbim_info {
+       enum mbim_state         state;
+       int                     enable_roaming;
+#define MBIM_PIN_REQUIRED      0
+#define MBIM_PIN_UNLOCKED      1
+#define MBIM_PUK_REQUIRED      2
+       int                     pin_state;
+       int                     pin_attempts_left;
+       int                     activation;
+       int                     sim_state;
+       int                     regstate;
+       int                     regmode;
+       int                     nwerror;
+       int                     packetstate;
+       uint32_t                supportedclasses; /* what the hw supports */
+       uint32_t                preferredclasses; /* what the user prefers */
+       uint32_t                highestclass;   /* what the network offers */
+       uint32_t                cellclass;
+#define MBIM_PROVIDERNAME_MAXLEN       20
+       uint16_t                provider[MBIM_PROVIDERNAME_MAXLEN];
+#define MBIM_PHONENR_MAXLEN            22
+       uint16_t                pn[MBIM_PHONENR_MAXLEN];
+#define MBIM_SUBSCRIBERID_MAXLEN       15
+       uint16_t                sid[MBIM_SUBSCRIBERID_MAXLEN];
+#define MBIM_ICCID_MAXLEN              20
+       uint16_t                iccid[MBIM_ICCID_MAXLEN];
+#define MBIM_ROAMINGTEXT_MAXLEN                63
+       uint16_t                roamingtxt[MBIM_ROAMINGTEXT_MAXLEN];
+
+#define MBIM_DEVID_MAXLEN              18
+       uint16_t                devid[MBIM_DEVID_MAXLEN];
+#define MBIM_FWINFO_MAXLEN             30
+       uint16_t                fwinfo[MBIM_FWINFO_MAXLEN];
+#define MBIM_HWINFO_MAXLEN             30
+       uint16_t                hwinfo[MBIM_HWINFO_MAXLEN];
+
+       uint16_t                apn[MBIM_APN_MAXLEN];
+       int                     apnlen;
+
+#define MBIM_VALUE_UNKNOWN     -999
+       int                     rssi;
+#define MBIM_BER_EXCELLENT     0
+#define MBIM_BER_VERYGOOD      1
+#define MBIM_BER_GOOD          2
+#define MBIM_BER_OK            3
+#define MBIM_BER_MEDIUM                4
+#define MBIM_BER_BAD           5
+#define MBIM_BER_VERYBAD       6
+#define MBIM_BER_EXTREMELYBAD  7
+       int                     ber;
+
+       int                     hw_radio_on;
+       int                     sw_radio_on;
+
+       uint64_t                uplink_speed;
+       uint64_t                downlink_speed;
+
+#define MBIM_MAX_DNSSRV                2
+       u_int32_t               ipv4dns[MBIM_MAX_DNSSRV];
+};
+
+#ifdef _KERNEL
+/*
+ * MBIM device
+ */
+struct mbim_softc {
+       struct device            sc_dev;
+       struct ifnet             sc_if;
+#define GET_IFP(sc)    (&(sc)->sc_if)
+       struct usbd_device      *sc_udev;
+
+       int                      sc_ver_maj;
+       int                      sc_ver_min;
+       int                      sc_ctrl_len;
+       int                      sc_maxpktlen;
+       int                      sc_maxsessions;
+
+       struct usb_task          sc_mbim_task;
+       struct usb_task          sc_get_response_task;
+       int                      sc_nresp;
+       struct timeout           sc_statechg_timer;
+
+       uint8_t                  sc_ctrl_ifaceno;
+       struct usbd_pipe        *sc_ctrl_pipe;
+       struct usb_cdc_notification sc_intr_msg;
+       struct usbd_interface   *sc_data_iface;
+
+       void                    *sc_resp_buf;
+       void                    *sc_ctrl_msg;
+
+       int                      sc_rx_ep;
+       struct usbd_xfer        *sc_rx_xfer;
+       void                    *sc_rx_buf;
+       struct usbd_pipe        *sc_rx_pipe;
+       unsigned                 sc_rx_nerr;
+       struct timeval           sc_rx_ratechk;
+
+       int                      sc_tx_ep;
+       struct usbd_xfer        *sc_tx_xfer;
+       void                    *sc_tx_buf;
+       struct usbd_pipe        *sc_tx_pipe;
+       struct mbuf             *sc_tx_m;
+       uint32_t                 sc_tx_seq;
+
+       uint32_t                 sc_tid;
+
+#define MBIM_IFADDR_NONE       ((void *)-1)
+       void                    *sc_saved_ifaddr;
+
+#define sc_state               sc_info.state
+#define sc_roaming             sc_info.enable_roaming
+       struct mbim_info         sc_info;
+};
+#endif /* _KERNEL */
Index: sys/dev/usb/mbim.h
===================================================================
RCS file: sys/dev/usb/mbim.h
diff -N sys/dev/usb/mbim.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ sys/dev/usb/mbim.h  23 May 2016 09:50:08 -0000
@@ -0,0 +1,669 @@
+/*     $OpenBSD$ */
+
+/*
+ * Copyright (c) 2016 genua mbH
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Mobile Broadband Interface Model
+ * http://www.usb.org/developers/docs/devclass_docs/MBIM-Compliance-1.0.pdf
+ */
+#ifndef _MBIM_H_
+#define _MBIM_H_
+
+#define UDESCSUB_MBIM                  27
+#define MBIM_INTERFACE_ALTSETTING      1
+
+#define MBIM_RESET_FUNCTION            0x05
+
+/*
+ * Registration state (MBIM_REGISTER_STATE)
+ */
+#define MBIM_REGSTATE_UNKNOWN                  0
+#define MBIM_REGSTATE_DEREGISTERED             1
+#define MBIM_REGSTATE_SEARCHING                        2
+#define MBIM_REGSTATE_HOME                     3
+#define MBIM_REGSTATE_ROAMING                  4
+#define MBIM_REGSTATE_PARTNER                  5
+#define MBIM_REGSTATE_DENIED                   6
+
+/*
+ * Data classes mask (MBIM_DATA_CLASS)
+ */
+#define MBIM_DATACLASS_NONE                    0x00000000
+#define MBIM_DATACLASS_GPRS                    0x00000001
+#define MBIM_DATACLASS_EDGE                    0x00000002
+#define MBIM_DATACLASS_UMTS                    0x00000004
+#define MBIM_DATACLASS_HSDPA                   0x00000008
+#define MBIM_DATACLASS_HSUPA                   0x00000010
+#define MBIM_DATACLASS_LTE                     0x00000020
+#define MBIM_DATACLASS_1XRTT                   0x00010000
+#define MBIM_DATACLASS_1XEVDO                  0x00020000
+#define MBIM_DATACLASS_1XEVDO_REV_A            0x00040000
+#define MBIM_DATACLASS_1XEVDV                  0x00080000
+#define MBIM_DATACLASS_3XRTT                   0x00100000
+#define MBIM_DATACLASS_1XEVDO_REV_B            0x00200000
+#define MBIM_DATACLASS_UMB                     0x00400000
+#define MBIM_DATACLASS_CUSTOM                  0x80000000
+
+/*
+ * Cell classes mask (MBIM_CELLULAR_CLASS)
+ */
+#define MBIM_CELLCLASS_GSM                     0x00000001
+#define MBIM_CELLCLASS_CDMA                    0x00000002
+
+/*
+ * UUIDs
+ */
+#define MBIM_UUID_LEN          16
+
+#define MBIM_UUID_BASIC_CONNECT {                              \
+               0xa2, 0x89, 0xcc, 0x33, 0xbc, 0xbb, 0x8b, 0x4f, \
+               0xb6, 0xb0, 0x13, 0x3e, 0xc2, 0xaa, 0xe6, 0xdf  \
+       }
+
+#define MBIM_UUID_CONTEXT_INTERNET {                           \
+               0x7e, 0x5e, 0x2a, 0x7e, 0x4e, 0x6f, 0x72, 0x72, \
+               0x73, 0x6b, 0x65, 0x6e, 0x7e, 0x5e, 0x2a, 0x7e  \
+       }
+
+#define MBIM_UUID_CONTEXT_VPN {                                \
+               0x9b, 0x9f, 0x7b, 0xbe, 0x89, 0x52, 0x44, 0xb7, \
+               0x83, 0xac, 0xca, 0x41, 0x31, 0x8d, 0xf7, 0xa0  \
+       }
+
+#define MBIM_CTRLMSG_MINLEN            64
+#define MBIM_CTRLMSG_MAXLEN            (4 * 1204)
+
+#define MBIM_MAXSEGSZ_MINVAL           (2 * 1024)
+
+/*
+ * Control messages (host to function)
+ */
+#define MBIM_OPEN_MSG                  1U
+#define MBIM_CLOSE_MSG                 2U
+#define MBIM_COMMAND_MSG               3U
+#define MBIM_HOST_ERROR_MSG            4U
+
+/*
+ * Control messages (function to host)
+ */
+#define MBIM_OPEN_DONE                 0x80000001U
+#define MBIM_CLOSE_DONE                        0x80000002U
+#define MBIM_COMMAND_DONE              0x80000003U
+#define MBIM_FUNCTION_ERROR_MSG                0x80000004U
+#define MBIM_INDICATE_STATUS_MSG       0x80000007U
+
+/*
+ * Generic status codes
+ */
+#define MBIM_STATUS_SUCCESS                    0
+#define MBIM_STATUS_BUSY                       1
+#define MBIM_STATUS_FAILURE                    2
+#define MBIM_STATUS_SIM_NOT_INSERTED           3
+#define MBIM_STATUS_BAD_SIM                    4
+#define MBIM_STATUS_PIN_REQUIRED               5
+#define MBIM_STATUS_PIN_DISABLED               6
+#define MBIM_STATUS_NOT_REGISTERED             7
+#define MBIM_STATUS_PROVIDERS_NOT_FOUND                8
+#define MBIM_STATUS_NO_DEVICE_SUPPORT          9
+#define MBIM_STATUS_PROVIDER_NOT_VISIBLE       10
+#define MBIM_STATUS_DATA_CLASS_NOT_AVAILABLE   11
+#define MBIM_STATUS_PACKET_SERVICE_DETACHED    12
+#define MBIM_STATUS_MAX_ACTIVATED_CONTEXTS     13
+#define MBIM_STATUS_NOT_INITIALIZED            14
+#define MBIM_STATUS_VOICE_CALL_IN_PROGRESS     15
+#define MBIM_STATUS_CONTEXT_NOT_ACTIVATED      16
+#define MBIM_STATUS_SERVICE_NOT_ACTIVATED      17
+#define MBIM_STATUS_INVALID_ACCESS_STRING      18
+#define MBIM_STATUS_INVALID_USER_NAME_PWD      19
+#define MBIM_STATUS_RADIO_POWER_OFF            20
+#define MBIM_STATUS_INVALID_PARAMETERS         21
+#define MBIM_STATUS_READ_FAILURE               22
+#define MBIM_STATUS_WRITE_FAILURE              23
+#define MBIM_STATUS_NO_PHONEBOOK               25
+#define MBIM_STATUS_PARAMETER_TOO_LONG         26
+#define MBIM_STATUS_STK_BUSY                   27
+#define MBIM_STATUS_OPERATION_NOT_ALLOWED      28
+#define MBIM_STATUS_MEMORY_FAILURE             29
+#define MBIM_STATUS_INVALID_MEMORY_INDEX       30
+#define MBIM_STATUS_MEMORY_FULL                        31
+#define MBIM_STATUS_FILTER_NOT_SUPPORTED       32
+#define MBIM_STATUS_DSS_INSTANCE_LIMIT         33
+#define MBIM_STATUS_INVALID_DEVICE_SERVICE_OPERATION   34
+#define MBIM_STATUS_AUTH_INCORRECT_AUTN                35
+#define MBIM_STATUS_AUTH_SYNC_FAILURE          36
+#define MBIM_STATUS_AUTH_AMF_NOT_SET           37
+#define MBIM_STATUS_CONTEXT_NOT_SUPPORTED      38
+#define MBIM_STATUS_SMS_UNKNOWN_SMSC_ADDRESS   100
+#define MBIM_STATUS_SMS_NETWORK_TIMEOUT                101
+#define MBIM_STATUS_SMS_LANG_NOT_SUPPORTED     102
+#define MBIM_STATUS_SMS_ENCODING_NOT_SUPPORTED 103
+#define MBIM_STATUS_SMS_FORMAT_NOT_SUPPORTED   104
+
+/*
+ * Message formats
+ */
+struct mbim_msghdr {
+       /* Msg header */
+       uint32_t        type;           /* message type */
+       uint32_t        len;            /* message length */
+       uint32_t        tid;            /* transaction id */
+} __packed;
+
+struct mbim_fraghdr {
+       uint32_t        nfrag;          /* total # of fragments */
+       uint32_t        currfrag;       /* current fragment */
+} __packed;
+
+struct mbim_fragmented_msg_hdr {
+       struct mbim_msghdr      hdr;
+       struct mbim_fraghdr     frag;
+} __packed;
+
+struct mbim_h2f_openmsg {
+       struct mbim_msghdr      hdr;
+       uint32_t                maxlen;
+} __packed;
+
+struct mbim_h2f_closemsg {
+       struct mbim_msghdr      hdr;
+} __packed;
+
+struct mbim_h2f_cmd {
+       struct mbim_msghdr      hdr;
+       struct mbim_fraghdr     frag;
+       uint8_t                 devid[MBIM_UUID_LEN];
+       uint32_t                cid;            /* command id */
+#define MBIM_CMDOP_QRY         0
+#define MBIM_CMDOP_SET         1
+       uint32_t                op;
+       uint32_t                infolen;
+       uint8_t                 info[];
+} __packed;
+
+struct mbim_f2h_indicate_status {
+       struct mbim_msghdr      hdr;
+       struct mbim_fraghdr     frag;
+       uint8_t                 devid[MBIM_UUID_LEN];
+       uint32_t                cid;            /* command id */
+       uint32_t                infolen;
+       uint8_t                 info[];
+} __packed;
+
+struct mbim_f2h_hosterr {
+       struct mbim_msghdr      hdr;
+
+#define MBIM_ERROR_TIMEOUT_FRAGMENT            1
+#define MBIM_ERROR_FRAGMENT_OUT_OF_SEQUENCE    2
+#define MBIM_ERROR_LENGTH_MISMATCH             3
+#define MBIM_ERROR_DUPLICATED_TID              4
+#define MBIM_ERROR_NOT_OPENED                  5
+#define MBIM_ERROR_UNKNOWN                     6
+#define MBIM_ERROR_CANCEL                      7
+#define MBIM_ERROR_MAX_TRANSFER                        8
+       uint32_t                err;
+} __packed;
+
+struct mbim_f2h_openclosedone {
+       struct mbim_msghdr      hdr;
+       int32_t                 status;
+} __packed;
+
+struct mbim_f2h_cmddone {
+       struct mbim_msghdr      hdr;
+       struct mbim_fraghdr     frag;
+       uint8_t                 devid[MBIM_UUID_LEN];
+       uint32_t                cid;            /* command id */
+       int32_t                 status;
+       uint32_t                infolen;
+       uint8_t                 info[];
+} __packed;
+
+/*
+ * Messages and commands for MBIM_UUID_BASIC_CONNECT
+ */
+#define MBIM_CID_DEVICE_CAPS                           1
+#define MBIM_CID_SUBSCRIBER_READY_STATUS               2
+#define MBIM_CID_RADIO_STATE                           3
+#define MBIM_CID_PIN                                   4
+#define MBIM_CID_PIN_LIST                              5
+#define MBIM_CID_HOME_PROVIDER                         6
+#define MBIM_CID_PREFERRED_PROVIDERS                   7
+#define MBIM_CID_VISIBLE_PROVIDERS                     8
+#define MBIM_CID_REGISTER_STATE                                9
+#define MBIM_CID_PACKET_SERVICE                                10
+#define MBIM_CID_SIGNAL_STATE                          11
+#define MBIM_CID_CONNECT                               12
+#define MBIM_CID_PROVISIONED_CONTEXTS                  13
+#define MBIM_CID_SERVICE_ACTIVATION                    14
+#define MBIM_CID_IP_CONFIGURATION                      15
+#define MBIM_CID_DEVICE_SERVICES                       16
+#define MBIM_CID_DEVICE_SERVICE_SUBSCRIBE_LIST         19
+#define MBIM_CID_PACKET_STATISTICS                     20
+#define MBIM_CID_NETWORK_IDLE_HINT                     21
+#define MBIM_CID_EMERGENCY_MODE                                22
+#define MBIM_CID_IP_PACKET_FILTERS                     23
+#define MBIM_CID_MULTICARRIER_PROVIDERS                        24
+
+struct mbim_cid_subscriber_ready_info {
+#define MBIM_SIMSTATE_NOTINITIALIZED           0
+#define MBIM_SIMSTATE_INITIALIZED              1
+#define MBIM_SIMSTATE_NOTINSERTED              2
+#define MBIM_SIMSTATE_BADSIM                   3
+#define MBIM_SIMSTATE_FAILURE                  4
+#define MBIM_SIMSTATE_NOTACTIVATED             5
+#define MBIM_SIMSTATE_LOCKED                   6
+       uint32_t        ready;
+
+       uint32_t        sid_offs;
+       uint32_t        sid_size;
+
+       uint32_t        icc_offs;
+       uint32_t        icc_size;
+
+#define MBIM_SIMUNIQEID_NONE                   0
+#define MBIM_SIMUNIQEID_PROTECT                        1
+       uint32_t        info;
+
+       uint32_t        no_pn;
+       struct {
+               uint32_t        offs;
+               uint32_t        size;
+       }
+                       pn[];
+} __packed;
+
+struct mbim_cid_radio_state {
+#define MBIM_RADIO_STATE_OFF                   0
+#define MBIM_RADIO_STATE_ON                    1
+       uint32_t        state;
+} __packed;
+
+struct mbim_cid_radio_state_info {
+       uint32_t        hw_state;
+       uint32_t        sw_state;
+} __packed;
+
+struct mbim_cid_pin {
+#define MBIM_PIN_TYPE_NONE                     0
+#define MBIM_PIN_TYPE_CUSTOM                   1
+#define MBIM_PIN_TYPE_PIN1                     2
+#define MBIM_PIN_TYPE_PIN2                     3
+#define MBIM_PIN_TYPE_DEV_SIM_PIN              4
+#define MBIM_PIN_TYPE_DEV_FIRST_SIM_PIN                5
+#define MBIM_PIN_TYPE_NETWORK_PIN              6
+#define MBIM_PIN_TYPE_NETWORK_SUBSET_PIN       7
+#define MBIM_PIN_TYPE_SERVICE_PROVIDER_PIN     8
+#define MBIM_PIN_TYPE_CORPORATE_PIN            9
+#define MBIM_PIN_TYPE_SUBSIDY_LOCK             10
+#define MBIM_PIN_TYPE_PUK1                     11
+#define MBIM_PIN_TYPE_PUK2                     12
+#define MBIM_PIN_TYPE_DEV_FIRST_SIM_PUK                13
+#define MBIM_PIN_TYPE_NETWORK_PUK              14
+#define MBIM_PIN_TYPE_NETWORK_SUBSET_PUK       15
+#define MBIM_PIN_TYPE_SERVICE_PROVIDER_PUK     16
+#define MBIM_PIN_TYPE_CORPORATE_PUK            17
+       uint32_t        type;
+
+#define MBIM_PIN_OP_ENTER                      0
+#define MBIM_PIN_OP_ENABLE                     1
+#define MBIM_PIN_OP_DISABLE                    2
+#define MBIM_PIN_OP_CHANGE                     3
+       uint32_t        op;
+       uint32_t        pin_offs;
+       uint32_t        pin_size;
+       uint32_t        newpin_offs;
+       uint32_t        newpin_size;
+#define MBIM_PIN_MAXLEN                32
+       uint8_t         data[2 * MBIM_PIN_MAXLEN];
+} __packed;
+
+struct mbim_cid_pin_info {
+       uint32_t        type;
+
+#define MBIM_PIN_STATE_UNLOCKED                        0
+#define MBIM_PIN_STATE_LOCKED                  1
+       uint32_t        state;
+       uint32_t        remaining_attempts;
+} __packed;
+
+struct mbim_cid_pin_list_info {
+       struct mbim_pin_desc {
+
+#define MBIM_PINMODE_NOTSUPPORTED              0
+#define MBIM_PINMODE_ENABLED                   1
+#define MBIM_PINMODE_DISABLED                  2
+               uint32_t        mode;
+
+#define MBIM_PINFORMAT_UNKNOWN                 0
+#define MBIM_PINFORMAT_NUMERIC                 1
+#define MBIM_PINFORMAT_ALPHANUMERIC            2
+               uint32_t        format;
+
+               uint32_t        minlen;
+               uint32_t        maxlen;
+       }
+               pin1,
+               pin2,
+               dev_sim_pin,
+               first_dev_sim_pin,
+               net_pin,
+               net_sub_pin,
+               svp_pin,
+               corp_pin,
+               subsidy_lock,
+               custom;
+} __packed;
+
+struct mbim_cid_device_caps {
+#define MBIM_DEVTYPE_UNKNOWN                   0
+#define MBIM_DEVTYPE_EMBEDDED                  1
+#define MBIM_DEVTYPE_REMOVABLE                 2
+#define MBIM_DEVTYPE_REMOTE                    3
+       uint32_t        devtype;
+
+       uint32_t        cellclass;      /* values: MBIM_CELLULAR_CLASS */
+       uint32_t        voiceclass;
+       uint32_t        simclass;
+       uint32_t        dataclass;      /* values: MBIM_DATA_CLASS */
+       uint32_t        smscaps;
+       uint32_t        cntrlcaps;
+       uint32_t        max_sessions;
+
+       uint32_t        custdataclass_offs;
+       uint32_t        custdataclass_size;
+
+       uint32_t        devid_offs;
+       uint32_t        devid_size;
+
+       uint32_t        fwinfo_offs;
+       uint32_t        fwinfo_size;
+
+       uint32_t        hwinfo_offs;
+       uint32_t        hwinfo_size;
+
+       uint32_t        data[];
+} __packed;
+
+struct mbim_cid_registration_state {
+       uint32_t        provid_offs;
+       uint32_t        provid_size;
+
+#define MBIM_REGACTION_AUTOMATIC               0
+#define MBIM_REGACTION_MANUAL                  1
+       uint32_t        regaction;
+       uint32_t        data_class;
+
+       uint32_t        data[];
+} __packed;
+
+struct mbim_cid_registration_state_info {
+       uint32_t        nwerror;
+
+       uint32_t        regstate;       /* values: MBIM_REGISTER_STATE */
+
+#define MBIM_REGMODE_UNKNOWN                   0
+#define MBIM_REGMODE_AUTOMATIC                 1
+#define MBIM_REGMODE_MANUAL                    2
+       uint32_t        regmode;
+
+       uint32_t        availclasses;   /* values: MBIM_DATA_CLASS */
+       uint32_t        curcellclass;   /* values: MBIM_CELLULAR_CLASS */
+
+       uint32_t        provid_offs;
+       uint32_t        provid_size;
+
+       uint32_t        provname_offs;
+       uint32_t        provname_size;
+
+       uint32_t        roamingtxt_offs;
+       uint32_t        roamingtxt_size;
+
+#define MBIM_REGFLAGS_NONE                     0
+#define MBIM_REGFLAGS_MANUAL_NOT_AVAILABLE     1
+#define MBIM_REGFLAGS_PACKETSERVICE_AUTOATTACH 2
+       uint32_t        regflag;
+
+       uint32_t        data[];
+} __packed;
+
+struct mbim_cid_packet_service {
+#define MBIM_PKTSERVICE_ACTION_ATTACH  0
+#define MBIM_PKTSERVICE_ACTION_DETACH  1
+       uint32_t        action;
+} __packed;
+
+struct mbim_cid_packet_service_info {
+       uint32_t        nwerror;
+
+#define MBIM_PKTSERVICE_STATE_UNKNOWN  0
+#define MBIM_PKTSERVICE_STATE_ATTACHING        1
+#define MBIM_PKTSERVICE_STATE_ATTACHED 2
+#define MBIM_PKTSERVICE_STATE_DETACHING        3
+#define MBIM_PKTSERVICE_STATE_DETACHED 4
+       uint32_t        state;
+
+       uint32_t        highest_dataclass;
+       uint64_t        uplink_speed;
+       uint64_t        downlink_speed;
+} __packed;
+
+struct mbim_cid_signal_state {
+       uint32_t        rssi;
+       uint32_t        err_rate;
+       uint32_t        ss_intvl;
+       uint32_t        rssi_thr;
+       uint32_t        err_thr;
+} __packed;
+
+struct mbim_cid_connect {
+       uint32_t        sessionid;
+
+#define MBIM_CONNECT_DEACTIVATE                0
+#define MBIM_CONNECT_ACTIVATE          1
+       uint32_t        command;
+
+#define MBIM_ACCESS_MAXLEN             200
+       uint32_t        access_offs;
+       uint32_t        access_size;
+
+#define MBIM_USER_MAXLEN               510
+       uint32_t        user_offs;
+       uint32_t        user_size;
+
+#define MBIM_PASSWD_MAXLEN             510
+       uint32_t        passwd_offs;
+       uint32_t        passwd_size;
+
+#define MBIM_COMPRESSION_NONE          0
+#define MBIM_COMPRESSION_ENABLE                1
+       uint32_t        compression;
+
+#define MBIM_AUTHPROT_NONE             0
+#define MBIM_AUTHPROT_PAP              1
+#define MBIM_AUTHPROT_CHAP             2
+#define MBIM_AUTHPROT_MSCHAP           3
+       uint32_t        authprot;
+
+#define MBIM_CONTEXT_IPTYPE_DEFAULT    0
+#define MBIM_CONTEXT_IPTYPE_IPV4       1
+#define MBIM_CONTEXT_IPTYPE_IPV6       2
+#define MBIM_CONTEXT_IPTYPE_IPV4V6     3
+#define MBIM_CONTEXT_IPTYPE_IPV4ANDV6  4
+       uint32_t        iptype;
+
+       uint8_t         context[MBIM_UUID_LEN];
+
+       uint8_t         data[MBIM_ACCESS_MAXLEN + MBIM_USER_MAXLEN +
+                            MBIM_PASSWD_MAXLEN];
+
+} __packed;
+
+struct mbim_cid_connect_info {
+       uint32_t        sessionid;
+
+#define MBIM_ACTIVATION_STATE_UNKNOWN          0
+#define MBIM_ACTIVATION_STATE_ACTIVATED                1
+#define MBIM_ACTIVATION_STATE_ACTIVATING       2
+#define MBIM_ACTIVATION_STATE_DEACTIVATED      3
+#define MBIM_ACTIVATION_STATE_DEACTIVATING     4
+       uint32_t        activation;
+
+       uint32_t        voice;
+       uint32_t        iptype;
+       uint8_t         context[MBIM_UUID_LEN];
+       uint32_t        nwerror;
+} __packed;
+
+struct mbim_cid_ipv4_element {
+       uint32_t        prefixlen;
+       uint32_t        addr;
+} __packed;
+
+struct mbim_cid_ipv6_element {
+       uint32_t        prefixlen;
+       uint8_t         addr[16];
+} __packed;
+
+struct mbim_cid_ip_configuration_info {
+       uint32_t        sessionid;
+
+#define MBIM_IPCONF_HAS_ADDRINFO       0x0001
+#define MBIM_IPCONF_HAS_GWINFO         0x0002
+#define MBIM_IPCONF_HAS_DNSINFO                0x0004
+#define MBIM_IPCONF_HAS_MTUINFO                0x0008
+       uint32_t        ipv4_available;
+       uint32_t        ipv6_available;
+
+       uint32_t        ipv4_naddr;
+       uint32_t        ipv4_addroffs;
+       uint32_t        ipv6_naddr;
+       uint32_t        ipv6_addroffs;
+
+       uint32_t        ipv4_gwoffs;
+       uint32_t        ipv6_gwoffs;
+
+       uint32_t        ipv4_ndnssrv;
+       uint32_t        ipv4_dnssrvoffs;
+       uint32_t        ipv6_ndnssrv;
+       uint32_t        ipv6_dnssrvoffs;
+
+       uint32_t        ipv4_mtu;
+       uint32_t        ipv6_mtu;
+
+       uint32_t        data[];
+} __packed;
+
+struct mbim_cid_packet_statistics_info {
+       uint32_t        in_discards;
+       uint32_t        in_errors;
+       uint64_t        in_octets;
+       uint64_t        in_packets;
+       uint64_t        out_octets;
+       uint64_t        out_packets;
+       uint32_t        out_errors;
+       uint32_t        out_discards;
+} __packed;
+
+
+#ifdef _KERNEL
+
+struct mbim_descriptor {
+       uByte   bLength;
+       uByte   bDescriptorType;
+       uByte   bDescriptorSubtype;
+#define MBIM_VER_MAJOR(v)      (((v) >> 8) & 0x0f)
+#define MBIM_VER_MINOR(v)      ((v) & 0x0f)
+       uWord   bcdMBIMVersion;
+       uWord   wMaxControlMessage;
+       uByte   bNumberFilters;
+       uByte   bMaxFilterSize;
+       uWord   wMaxSegmentSize;
+       uByte   bmNetworkCapabilities;
+} __packed;
+
+/*
+ * NCM Encoding
+ */
+#define MBIM_HDR16_LEN         \
+       (sizeof (struct ncm_header16) + sizeof (struct ncm_pointer16))
+#define MBIM_HDR32_LEN         \
+       (sizeof (struct ncm_header32) + sizeof (struct ncm_pointer32))
+
+struct ncm_header16 {
+#define NCM_HDR16_SIG          0x484d434e
+       uDWord  dwSignature;
+       uWord   wHeaderLength;
+       uWord   wSequence;
+       uWord   wBlockLength;
+       uWord   wNdpIndex;
+} __packed;
+
+struct ncm_header32 {
+#define NCM_HDR32_SIG          0x686d636e
+       uDWord  dwSignature;
+       uWord   wHeaderLength;
+       uWord   wSequence;
+       uDWord  dwBlockLength;
+       uDWord  dwNdpIndex;
+} __packed;
+
+
+#define MBIM_NCM_NTH_SIDSHIFT  24
+#define MBIM_NCM_NTH_GETSID(s) (((s) > MBIM_NCM_NTH_SIDSHIFT) & 0xff)
+
+struct ncm_pointer16_dgram {
+       uWord   wDatagramIndex;
+       uWord   wDatagramLen;
+} __packed;
+
+struct ncm_pointer16 {
+#define MBIM_NCM_NTH16_IPS     0x00535049
+#define MBIM_NCM_NTH16_ISISG(s)        (((s) & 0x00ffffff) == 
MBIM_NCM_NTH16_IPS)
+#define MBIM_NCM_NTH16_SIG(s)  \
+               ((((s) & 0xff) << MBIM_NCM_NTH_SIDSHIFT) | MBIM_NCM_NTH16_IPS)
+       uDWord  dwSignature;
+       uWord   wLength;
+       uWord   wNextNdpIndex;
+
+       /* Minimum is two datagrams, but can be more */
+       struct ncm_pointer16_dgram dgram[2];
+} __packed;
+
+struct ncm_pointer32_dgram {
+       uDWord  dwDatagramIndex;
+       uDWord  dwDatagramLen;
+} __packed;
+
+struct ncm_pointer32 {
+#define MBIM_NCM_NTH32_IPS     0x00737069
+#define MBIM_NCM_NTH32_ISISG(s)        (((s) & 0x00ffffff) == 
MBIM_NCM_NTH32_IPS)
+#define MBIM_NCM_NTH32_SIG(s)  \
+               ((((s) & 0xff) << MBIM_NCM_NTH_SIDSHIFT) | MBIM_NCM_NTH32_IPS)
+       uDWord  dwSignature;
+       uWord   wLength;
+       uWord   wReserved6;
+       uDWord  dwNextNdpIndex;
+       uDWord  dwReserved12;
+
+       /* Minimum is two datagrams, but can be more */
+       struct ncm_pointer32_dgram dgram[2];
+} __packed;
+
+#endif /* _KERNEL */
+
+#endif /* _MBIM_H_ */

Reply via email to