FCC Auth patch for umb(4)

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

FCC Auth patch for umb(4)

Gerhard Roth-2
Some MBIM devices need a FCC Authentication before they're willing to
turn on the radio. This has to be done by sending a QMI command inside
an MBIM message.

This patch is based on an earlier patch by Stuart Henderson. One
crucial thing was missing in sthen@'s patch: first a client-id (CID)
has to be allocated and this CID must then be patched into the
right field of the FCC-Auth.

Sending the FCC-Auth is limited to a list of devices known to require
this. Currently, this is only the Sierra Wireless EM7455.

This patch was possible thanks to a lot of testing by Bryan Vyhmeister.


Gerhard


Index: sys/dev/usb/if_umb.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_umb.c,v
retrieving revision 1.6
diff -u -p -u -p -r1.6 if_umb.c
--- sys/dev/usb/if_umb.c 14 Nov 2016 12:55:56 -0000 1.6
+++ sys/dev/usb/if_umb.c 16 Nov 2016 08:42:44 -0000
@@ -170,6 +170,8 @@ int umb_setpin(struct umb_softc *, int
     int);
 void umb_setdataclass(struct umb_softc *);
 void umb_radio(struct umb_softc *, int);
+void umb_allocate_cid(struct umb_softc *);
+void umb_send_fcc_auth(struct umb_softc *);
 void umb_packet_service(struct umb_softc *, int);
 void umb_connect(struct umb_softc *);
 void umb_disconnect(struct umb_softc *);
@@ -177,8 +179,10 @@ void umb_send_connect(struct umb_softc
 
 void umb_qry_ipconfig(struct umb_softc *);
 void umb_cmd(struct umb_softc *, int, int, void *, int);
+void umb_cmd1(struct umb_softc *, int, int, void *, int, uint8_t *);
 void umb_command_done(struct umb_softc *, void *, int);
 void umb_decode_cid(struct umb_softc *, uint32_t, void *, int);
+void umb_decode_qmi(struct umb_softc *, uint8_t *, int);
 
 void umb_intr(struct usbd_xfer *, void *, usbd_status);
 
@@ -188,6 +192,7 @@ int umb_xfer_tout = USBD_DEFAULT_TIMEO
 
 uint8_t umb_uuid_basic_connect[] = MBIM_UUID_BASIC_CONNECT;
 uint8_t umb_uuid_context_internet[] = MBIM_UUID_CONTEXT_INTERNET;
+uint8_t umb_uuid_qmi_mbim[] = MBIM_UUID_QMI_MBIM;
 uint32_t umb_session_id = 0;
 
 struct cfdriver umb_cd = {
@@ -204,6 +209,39 @@ const struct cfattach umb_ca = {
 
 int umb_delay = 4000;
 
+/*
+ * These devices require an "FCC Authentication" command.
+ */
+const struct usb_devno umb_fccauth_devs[] = {
+ { USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_EM7455 },
+};
+
+uint8_t umb_qmi_alloc_cid[] = {
+ 0x01,
+ 0x0f, 0x00, /* len */
+ 0x00, /* QMUX flags */
+ 0x00, /* service "ctl" */
+ 0x00, /* CID */
+ 0x00, /* QMI flags */
+ 0x01, /* transaction */
+ 0x22, 0x00, /* msg "Allocate CID" */
+ 0x04, 0x00, /* TLV len */
+ 0x01, 0x01, 0x00, 0x02 /* TLV */
+};
+
+uint8_t umb_qmi_fcc_auth[] = {
+ 0x01,
+ 0x0c, 0x00, /* len */
+ 0x00, /* QMUX flags */
+ 0x02, /* service "dms" */
+#define UMB_QMI_CID_OFFS 5
+ 0x00, /* CID (filled in later) */
+ 0x00, /* QMI flags */
+ 0x01, 0x00, /* transaction */
+ 0x5f, 0x55, /* msg "Send FCC Authentication" */
+ 0x00, 0x00 /* TLV len */
+};
+
 int
 umb_match(struct device *parent, void *match, void *aux)
 {
@@ -328,6 +366,10 @@ umb_attach(struct device *parent, struct
  printf("%s: missing MBIM descriptor\n", DEVNAM(sc));
  goto fail;
  }
+ if (usb_lookup(umb_fccauth_devs, uaa->vendor, uaa->product)) {
+ sc->sc_flags |= UMBFLG_FCC_AUTH_REQUIRED;
+ sc->sc_cid = -1;
+ }
 
  for (i = 0; i < uaa->nifaces; i++) {
  if (usbd_iface_claimed(sc->sc_udev, i))
@@ -783,7 +825,14 @@ umb_statechg_timeout(void *arg)
 {
  struct umb_softc *sc = arg;
 
- printf("%s: state change timeout\n",DEVNAM(sc));
+ if (sc->sc_info.regstate == MBIM_REGSTATE_ROAMING && !sc->sc_roaming) {
+ /*
+ * Query the registration state until we're with the home
+ * network again.
+ */
+ umb_cmd(sc, MBIM_CID_REGISTER_STATE, MBIM_CMDOP_QRY, NULL, 0);
+ } else
+ printf("%s: state change timeout\n",DEVNAM(sc));
  usb_add_task(sc->sc_udev, &sc->sc_umb_task);
 }
 
@@ -863,8 +912,23 @@ umb_up(struct umb_softc *sc)
  umb_open(sc);
  break;
  case UMB_S_OPEN:
- DPRINTF("%s: init: turning radio on ...\n", DEVNAM(sc));
- umb_radio(sc, 1);
+ if (sc->sc_flags & UMBFLG_FCC_AUTH_REQUIRED) {
+ if (sc->sc_cid == -1) {
+ DPRINTF("%s: init: allocating CID ...\n",
+    DEVNAM(sc));
+ umb_allocate_cid(sc);
+ break;
+ } else
+ umb_newstate(sc, UMB_S_CID, UMB_NS_DONT_DROP);
+ } else {
+ DPRINTF("%s: init: turning radio on ...\n", DEVNAM(sc));
+ umb_radio(sc, 1);
+ break;
+ }
+ /*FALLTHROUGH*/
+ case UMB_S_CID:
+ DPRINTF("%s: init: sending FCC auth ...\n", DEVNAM(sc));
+ umb_send_fcc_auth(sc);
  break;
  case UMB_S_RADIO:
  DPRINTF("%s: init: checking SIM state ...\n", DEVNAM(sc));
@@ -936,6 +1000,7 @@ umb_down(struct umb_softc *sc, int force
  if (!force)
  break;
  /*FALLTHROUGH*/
+ case UMB_S_CID:
  case UMB_S_OPEN:
  case UMB_S_DOWN:
  /* Do not close the device */
@@ -1202,7 +1267,7 @@ umb_decode_register_state(struct umb_sof
  log(LOG_INFO,
     "%s: disconnecting from roaming network\n",
     DEVNAM(sc));
- umb_newstate(sc, UMB_S_ATTACHED, UMB_NS_DONT_RAISE);
+ umb_disconnect(sc);
  }
  return 1;
 }
@@ -2040,6 +2105,29 @@ umb_radio(struct umb_softc *sc, int on)
 }
 
 void
+umb_allocate_cid(struct umb_softc *sc)
+{
+ umb_cmd1(sc, MBIM_CID_DEVICE_CAPS, MBIM_CMDOP_SET,
+    umb_qmi_alloc_cid, sizeof (umb_qmi_alloc_cid), umb_uuid_qmi_mbim);
+}
+
+void
+umb_send_fcc_auth(struct umb_softc *sc)
+{
+ uint8_t fccauth[sizeof (umb_qmi_fcc_auth)];
+
+ if (sc->sc_cid == -1) {
+ DPRINTF("%s: missing CID, cannot send FCC auth\n", DEVNAM(sc));
+ umb_allocate_cid(sc);
+ return;
+ }
+ memcpy(fccauth, umb_qmi_fcc_auth, sizeof (fccauth));
+ fccauth[UMB_QMI_CID_OFFS] = sc->sc_cid;
+ umb_cmd1(sc, MBIM_CID_DEVICE_CAPS, MBIM_CMDOP_SET,
+    fccauth, sizeof (fccauth), umb_uuid_qmi_mbim);
+}
+
+void
 umb_packet_service(struct umb_softc *sc, int attach)
 {
  struct mbim_cid_packet_service s;
@@ -2120,6 +2208,13 @@ umb_qry_ipconfig(struct umb_softc *sc)
 void
 umb_cmd(struct umb_softc *sc, int cid, int op, void *data, int len)
 {
+ umb_cmd1(sc, cid, op, data, len, umb_uuid_basic_connect);
+}
+
+void
+umb_cmd1(struct umb_softc *sc, int cid, int op, void *data, int len,
+    uint8_t *uuid)
+{
  struct mbim_h2f_cmd *cmd;
  int totlen;
 
@@ -2132,7 +2227,7 @@ umb_cmd(struct umb_softc *sc, int cid, i
  cmd = sc->sc_ctrl_msg;
  memset(cmd, 0, sizeof (*cmd));
  cmd->frag.nfrag = htole32(1);
- memcpy(cmd->devid, umb_uuid_basic_connect, sizeof (cmd->devid));
+ memcpy(cmd->devid, uuid, sizeof (cmd->devid));
  cmd->cid = htole32(cid);
  cmd->op = htole32(op);
  cmd->infolen = htole32(len);
@@ -2152,6 +2247,7 @@ umb_command_done(struct umb_softc *sc, v
  uint32_t status;
  uint32_t cid;
  uint32_t infolen;
+ int qmimsg = 0;
 
  if (len < sizeof (*cmd)) {
  DPRINTF("%s: discard short %s messsage\n", DEVNAM(sc),
@@ -2160,10 +2256,14 @@ umb_command_done(struct umb_softc *sc, v
  }
  cid = letoh32(cmd->cid);
  if (memcmp(cmd->devid, umb_uuid_basic_connect, sizeof (cmd->devid))) {
- DPRINTF("%s: discard %s messsage for other UUID '%s'\n",
-    DEVNAM(sc), umb_request2str(letoh32(cmd->hdr.type)),
-    umb_uuid2str(cmd->devid));
- return;
+ if (memcmp(cmd->devid, umb_uuid_qmi_mbim,
+    sizeof (cmd->devid))) {
+ DPRINTF("%s: discard %s messsage for other UUID '%s'\n",
+    DEVNAM(sc), umb_request2str(letoh32(cmd->hdr.type)),
+    umb_uuid2str(cmd->devid));
+ return;
+ } else
+ qmimsg = 1;
  }
 
  status = letoh32(cmd->status);
@@ -2192,8 +2292,14 @@ umb_command_done(struct umb_softc *sc, v
     (int)sizeof (*cmd) + infolen, len);
  return;
  }
- DPRINTFN(2, "%s: set/qry %s done\n", DEVNAM(sc), umb_cid2str(cid));
- umb_decode_cid(sc, cid, cmd->info, infolen);
+ if (qmimsg) {
+ if (sc->sc_flags & UMBFLG_FCC_AUTH_REQUIRED)
+ umb_decode_qmi(sc, cmd->info, infolen);
+ } else {
+ DPRINTFN(2, "%s: set/qry %s done\n", DEVNAM(sc),
+    umb_cid2str(cid));
+ umb_decode_cid(sc, cid, cmd->info, infolen);
+ }
 }
 
 void
@@ -2241,6 +2347,122 @@ umb_decode_cid(struct umb_softc *sc, uin
  if (!ok)
  DPRINTF("%s: discard %s with bad info length %d\n",
     DEVNAM(sc), umb_cid2str(cid), len);
+ return;
+}
+
+void
+umb_decode_qmi(struct umb_softc *sc, uint8_t *data, int len)
+{
+ uint8_t srv;
+ uint16_t msg, tlvlen;
+ uint32_t val;
+
+#define UMB_QMI_QMUXLEN 6
+ if (len < UMB_QMI_QMUXLEN)
+ goto tooshort;
+
+ srv = data[4];
+ data += UMB_QMI_QMUXLEN;
+ len -= UMB_QMI_QMUXLEN;
+
+#define UMB_GET16(p) ((uint16_t)*p | (uint16_t)*(p + 1) << 8)
+#define UMB_GET32(p) ((uint32_t)*p | (uint32_t)*(p + 1) << 8 | \
+    (uint32_t)*(p + 2) << 16 |(uint32_t)*(p + 3) << 24)
+ switch (srv) {
+ case 0: /* ctl */
+#define UMB_QMI_CTLLEN 6
+ if (len < UMB_QMI_CTLLEN)
+ goto tooshort;
+ msg = UMB_GET16(&data[2]);
+ tlvlen = UMB_GET16(&data[4]);
+ data += UMB_QMI_CTLLEN;
+ len -= UMB_QMI_CTLLEN;
+ break;
+ case 2: /* dms  */
+#define UMB_QMI_DMSLEN 7
+ if (len < UMB_QMI_DMSLEN)
+ goto tooshort;
+ msg = UMB_GET16(&data[3]);
+ tlvlen = UMB_GET16(&data[5]);
+ data += UMB_QMI_DMSLEN;
+ len -= UMB_QMI_DMSLEN;
+ break;
+ default:
+ DPRINTF("%s: discard QMI message for unknown service type %d\n",
+    DEVNAM(sc), srv);
+ return;
+ }
+
+ if (len < tlvlen)
+ goto tooshort;
+
+#define UMB_QMI_TLVLEN 3
+ while (len > 0) {
+ if (len < UMB_QMI_TLVLEN)
+ goto tooshort;
+ tlvlen = UMB_GET16(&data[1]);
+ if (len < UMB_QMI_TLVLEN + tlvlen)
+ goto tooshort;
+ switch (data[0]) {
+ case 1: /* allocation info */
+ if (msg == 0x0022) { /* Allocate CID */
+ if (tlvlen != 2 || data[3] != 2) /* dms */
+ break;
+ sc->sc_cid = data[4];
+ DPRINTF("%s: QMI CID %d allocated\n",
+    DEVNAM(sc), sc->sc_cid);
+ umb_newstate(sc, UMB_S_CID, UMB_NS_DONT_DROP);
+ }
+ break;
+ case 2: /* response */
+ if (tlvlen != sizeof (val))
+ break;
+ val = UMB_GET32(&data[3]);
+ switch (msg) {
+ case 0x0022: /* Allocate CID */
+ if (val != 0) {
+ log(LOG_ERR, "%s: allocation of QMI CID"
+    " failed, error 0x%x\n", DEVNAM(sc),
+    val);
+ /* XXX how to proceed? */
+ return;
+ }
+ break;
+ case 0x555f: /* Send FCC Authentication */
+ if (val == 0)
+ log(LOG_INFO, "%s: send FCC "
+    "Authentication succeeded\n",
+    DEVNAM(sc));
+ else if (val == 0x001a0001)
+ log(LOG_INFO, "%s: FCC Authentication "
+    "not required\n", DEVNAM(sc));
+ else
+ log(LOG_INFO, "%s: send FCC "
+    "Authentication failed, "
+    "error 0x%x\n", DEVNAM(sc), val);
+
+ /* FCC Auth is needed only once after power-on*/
+ sc->sc_flags &= ~UMBFLG_FCC_AUTH_REQUIRED;
+
+ /* Try to proceed anyway */
+ DPRINTF("%s: init: turning radio on ...\n",
+    DEVNAM(sc));
+ umb_radio(sc, 1);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ data += UMB_QMI_TLVLEN + tlvlen;
+ len -= UMB_QMI_TLVLEN + tlvlen;
+ }
+ return;
+
+tooshort:
+ DPRINTF("%s: discard short QMI message\n", DEVNAM(sc));
  return;
 }
 
Index: sys/dev/usb/if_umb.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_umb.h,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 if_umb.h
--- sys/dev/usb/if_umb.h 15 Jun 2016 19:39:34 -0000 1.1
+++ sys/dev/usb/if_umb.h 19 Oct 2016 09:03:49 -0000
@@ -220,6 +220,7 @@ umb_val2descr(const struct umb_valdescr
 enum umb_state {
  UMB_S_DOWN = 0, /* interface down */
  UMB_S_OPEN, /* MBIM device has been opened */
+ UMB_S_CID, /* QMI client id allocated */
  UMB_S_RADIO, /* radio is on */
  UMB_S_SIMREADY, /* SIM is ready */
  UMB_S_ATTACHED, /* packet service is attached */
@@ -228,11 +229,12 @@ enum umb_state {
 };
 
 #define UMB_INTERNAL_STATE_DESCRIPTIONS { \
- { UMB_S_DOWN, "down" }, \
- { UMB_S_OPEN, "open" }, \
+ { UMB_S_DOWN, "down" }, \
+ { UMB_S_OPEN, "open" }, \
+ { UMB_S_CID, "CID allocated" }, \
  { UMB_S_RADIO, "radio on" }, \
  { UMB_S_SIMREADY, "SIM is ready" }, \
- { UMB_S_ATTACHED, "attached" }, \
+ { UMB_S_ATTACHED, "attached" }, \
  { UMB_S_CONNECTED, "connected" }, \
  { UMB_S_UP, "up" }, \
  { 0, NULL } }
@@ -336,6 +338,10 @@ struct umb_softc {
  int sc_ctrl_len;
  int sc_maxpktlen;
  int sc_maxsessions;
+
+#define UMBFLG_FCC_AUTH_REQUIRED 0x0001
+ uint32_t sc_flags;
+ int sc_cid;
 
  struct usb_task sc_umb_task;
  struct usb_task sc_get_response_task;
Index: sys/dev/usb/mbim.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/mbim.h,v
retrieving revision 1.1
diff -u -p -u -p -r1.1 mbim.h
--- sys/dev/usb/mbim.h 15 Jun 2016 19:39:34 -0000 1.1
+++ sys/dev/usb/mbim.h 19 Oct 2016 10:40:28 -0000
@@ -85,6 +85,11 @@
  0x83, 0xac, 0xca, 0x41, 0x31, 0x8d, 0xf7, 0xa0 \
  }
 
+#define MBIM_UUID_QMI_MBIM { \
+ 0xd1, 0xa3, 0x0b, 0xc2, 0xf9, 0x7a, 0x6e, 0x43, \
+ 0xbf, 0x65, 0xc7, 0xe2, 0x4f, 0xb0, 0xf0, 0xd3 \
+ }
+
 #define MBIM_CTRLMSG_MINLEN 64
 #define MBIM_CTRLMSG_MAXLEN (4 * 1204)